/* Copyright (C) 2016-2017 HangZhou Zenzet Technology Co., Ltd.
 * All right reserved

 * File:lktdatasync.cpp
 * Author:guojianchuan
 * Date:2017-05-19

 */

/* 系统头文件 */
#include <stdio.h>
#define _GNU_SOURCE
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <signal.h>

/* 依赖库头文件 */
#include "openssl/evp.h"
#include "openssl/err.h"
#include "mxml.h"
#include "zlog.h"
#include "curl.h"
#include "mysql.h"
#include "cJSON.h"


/* 本模块头文件 */
#include "lktcommon.h"
#include "lkterr.h"
#include "lktstruct.h"
#include "lktcipher.h"
#include "memcheck.h"

#define DBS_SQLRELAY_CONF               INSTALL_DIR"/etc/sqlrelay.conf.d/dbs.conf"
#define ZLOG_DATASYNC_CONF              INSTALL_DIR"/dbs/conf/datasync-zlog.conf"
#define ZLOG_DATASYNC_CAT               "datasync_cat"
#define STRATEGY_FILE                   INSTALL_DIR"/dbs/var/data/strategy.json"
#define DATASYNC_LOCK_FILE              INSTALL_DIR"/dbs/var/run/dbs-datasync.pid"

/* 数量宏 */
#define HOST_NAME_LEN                   256
#define USER_NAME_LEN                   64
#define PASSWORD_LEN                    64
#define TIME_LEN                        64
#define DATE_LEN                        64
#define URL_LEN                         512
#define HTTP_HEADER_LEN                 256
#define SQL_LEN                         256

#define DBS_HOST_LEN                    HOST_NAME_LEN
#define DBS_USER_LEN                    USER_NAME_LEN
#define DBS_PASSWORD_LEN                PASSWORD_LEN
#define AUTHOR_LEN                      128

#define MD5_BYTE_LEN                    16
#define MD5_BASE64_LEN                  64
#define HMAC_LEN                        128

#define HTTP_BUFFER_MAX_LEN             1024*10
#define DELTA_STATUS_BUF_LEN            512
#define QUERY_PAGE_COUNT                20



/* 字符串宏 */ 
#define ALGO_MD5                        "MD5"
#define ALGO_SHA1                       "SHA1"
#define HTTP_PROTOCOL                   ghttp_protocol
#define HEADER_SIGNATURE                "LKT_OPEN"
#define EMPTY_BODY_STRING               ""

#define IDENTIFY_MASTERKEY              "masterKey"
#define IDENTIFY_PV                     "pv"
#define IDENTIFY_PK                     "PK"
#define IDENTIFY_APPKEY                 "appKey"
#define IDENTIFY_COMPANY                "companyID"
#define IDENTIFY_APPSCRET               "appSecret"
#define IDENTIFY_DATE                   "date"
#define IDENTIFY_AUTHOR                 "Authorization"
#define IDENTIFY_LASTSYNC               "lastSyncAt"
#define IDENTIFY_CURSOR                 "cursor"
#define IDENTIFY_MAXCOUNT               "maxcount"
#define IDENTIFY_GET                    "GET"
#define IDENTIFY_CODE                   "code"
#define IDENTIFY_DBS_STRATEGY_CHANGES   "dbs_strategies_changes"
#define IDENTIFY_DATA                   "data"
#define IDENTIFY_DBS_STRATEGY           "dbs_strategies"
// 应答接口修改后strategy下的id等价于其子节点dataKey下的strategyID
// 获取策略id时只需获取id节点值即可
#define IDENTIFY_STRATEGYID             "id"
#define IDENTIFY_COLUMNNAME             "columnName"
#define IDENTIFY_TABLENAME              "tableName"
#define IDENTIFY_DBNAME                 "schemaName"
#define IDENTIFY_STATE                  "state"
// 应答接口修改后, datatype改为columntype datakey改为dbsKMSDataKeyDO
#define IDENTIFY_DATATYPE               "columnType"
#define IDENTIFY_SEARCHABLE             "searchable"
#define IDENTIFY_CHARSET                "charset"
#define IDENTIFY_DATAKEY                "dbsKMSDataKeyDO"
#define IDENTIFY_DATAKEY_KEY            "key"
#define IDENTIFY_DATAKEY_IV             "iv"
#define IDENTIFY_APP_DATAKEY            IDENTIFY_DATAKEY_KEY
#define IDENTIFY_APP_DATAIV             IDENTIFY_DATAKEY_IV
#define IDENTIFY_DATAKEY_MAPPINGID      "mappingID"
//协议修改后暂不使用 disableDecrypted字段, 是有启用字段加密由enabled表示改为state
//#define IDENTIFY_DATAKEY_DISABLEDECRYPT "disabledDecrypted"
#define IDENTIFY_DATAKEY_ENABLEENCRYPT  "state"
#define IDENTIFY_CIPHER                 "cipher"
#define IDENTIFY_CIPHER_ID              "id"
#define IDENTIFY_CIPHER_ALGO            "name"
#define IDENTIFY_CIPHER_MODE            "mode"
#define IDENTIFY_CIPHER_PADDING         "paddingMode"
#define IDENTIFY_CIPHER_KEY_LEN         "keyLength"
#define IDENTIFY_CIPHER_IV_LEN          "ivLength"

#define IDENTIFY_DEFAULT_CHARSET        "default"

 
/* API宏 */
#ifdef MOCK_TEST
#define MOCK_ID                         "/Qonw7ZnN.mock"
#define API_APP_CONSTANTS               MOCK_ID"/util/1/data/app_constants_config"
#define API_LONG_PULL                   MOCK_ID"/acc/1/dbs_data/longpull"
#define API_DBS_STRATEGY                MOCK_ID"/acc/1/dbs_data/dbs_strategy"
#define API_COMPANY_KEY                 MOCK_ID"/acc/1/data/single_company"
#define API_APP_DATAKEY                 MOCK_ID"/acc/1/dbs_data/app_config"
#else
#define API_APP_CONSTANTS               "/util/1/data/app_constants_config"
#define API_LONG_PULL                   "/acc/1/dbs_data/longpull"
#define API_DBS_STRATEGY                "/acc/1/dbs_data/dbs_strategy"
#define API_COMPANY_KEY                 "/acc/1/data/single_company"
#define API_APP_DATAKEY                 "/acc/1/dbs_data/app_config"
#endif

/* 时间宏 */
#define RUNNING_INTERVAL                5
#define UPDATE_INTERVAL                 10
#define MYSQL_CONNECT_TIMEOUT           10
#define MYSQL_READ_TIMEOUT              10
#define MYSQL_WRITE_TIMEOUT             10
#define CURL_CONNECT_TIMEOUT            10
#define CURL_TRANSFER_TIMEOUT           120

#define SUPPRESS_MAX_ERRORS             20


/**
* @struct: HTTP_RESP_CODE_E
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: HTTP返回码
*/
typedef enum tagHTTPRespCode
{
    HTTP_OK = 200
}HTTP_RESP_CODE_E;

/**
* @struct: RECV_BUFFER_S
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 用于接受CURL返回的HTTP数据
*/
typedef struct tagResponseData
{
    size_t writeLen;
    size_t bufferLen;
    char *content;
}RECV_BUFFER_S;

/**
* @struct: STRATEGY_S
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 用于存放JSON解析后的策略数据
*/
typedef struct tagStrategy
{
    int strategyID;
    char colName[COLUMN_NAME_MAX_LEN];
    char tblName[TABLE_NAME_MAX_LEN];
    char dbName[DATABASE_NAME_MAX_LEN];
    char datatype[COLUMN_TYPE_NAME_LEN];
    int state;
    int searchable;
    char charset[CHARSET_NAME_LEN];
    DATA_KEY_S datakey;
    CIPHER_S cipher;
}STRATEGY_S;

/**
* @struct: DELTA_S
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 用于存放JSON解析后的数据
*/
typedef struct tagDelta
{
    char appKey[ACCESSKEY_ID_MAX_LEN];
    int code;
    int cursor;
    long long lastSync;
    int strategyNum;
    STRATEGY_S *strategies;
}DELTA_S;

/**
* @struct: DBS_CONFIG_S
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 用于保存DBS的信息
*/
typedef struct tagDBSConfig
{
    char host[HOST_NAME_LEN];
    int port;
    char accKeyID[ACCESSKEY_ID_MAX_LEN];
    char accKeySecret[ACCESSKEY_SECRET_MAX_LEN];
    char dbsHost[DBS_HOST_LEN];
    int  dbsPort;
    char dbsUser[DBS_USER_LEN];
    char dbsPassword[DBS_PASSWORD_LEN];
}DBS_CONFIG_S;

/**
* @struct: DBS_S
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: DBS信息
*/
typedef struct tagDBS
{
    DBS_CONFIG_S dbsConfig; 
    RECV_BUFFER_S recvBuff;
    DATA_SHM_S *dataShm;
}DBS_S;

static DBS_S gdbsInfo;
static char* ghttp_protocol = "https";

static int _LKT_UpdateStrategyToSHM (DELTA_S *pdelta);
typedef void (*sighandler_t)(int);

/* MySQL自带的数据库 */
const char *mysqlOwnDB[] = {
    "information_schema",
    "mysql",
    "performance_schema"
};

/**
* @Function:_LKT_GetDBS
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 获取DBS指针
* @caution: 
*/
static DBS_S* _LKT_GetDBS (void)
{
    return &gdbsInfo;
}

/**
* @Function:_LKT_InitZlog
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 初始化zlog
* @caution: 只需要调用一次，不再使用后，调用_LKT_ReleaseZlog()释放资源;
*/
static int _LKT_InitZlog (void)
{
    int ret = ERR_COMMON_SUCCESS;
    
    ret = dzlog_init(ZLOG_DATASYNC_CONF, ZLOG_DATASYNC_CAT);
    if (ERR_COMMON_SUCCESS != ret) 
    {
        printf("init zlog failed\n");
        return ERR_COMMON_FAILED;
    }
        
    return ret;
}

/**
* @Function:_LKT_ReleaseZlog
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 释放zlog
* @caution: 
*/
static void _LKT_ReleaseZlog (void)
{
    zlog_fini();
    return;
}

/**
* @Function:_LKT_LoadXML
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 加载XML文件
* @caution: 
*/
static mxml_node_t* _LKT_LoadXML (const char* xmlpath)
{
    if (NULL == xmlpath)
    {
        return NULL;
    }
    
    FILE *fp = NULL;
    mxml_node_t *tree = NULL;
    
    do
    {
        fp = fopen(xmlpath, "r");
        if (NULL == fp)
        {
            dzlog_error("open %s failed", xmlpath);
            break;
        }
        
        tree = mxmlLoadFile(NULL, fp, MXML_TEXT_CALLBACK);
        if (NULL == tree)
        {
            dzlog_error( "mxmlLoadFile failed. %s", xmlpath);
            break;
        }

    } while (0);
        
    if (NULL != fp)
    {
        fclose(fp);
        fp = NULL;
    }

    return tree;
}

/**
* @Function:_LKT_UnloadXML
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 卸载XML文件
* @caution: 
*/
static void _LKT_UnloadXML (mxml_node_t *tree)
{
    if (NULL != tree)
    {
        mxmlDelete(tree);
        tree = NULL;
    }

    return;
}

/**
* @Function:_LKT_InitDBSConfig
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 初始化dbs配置
* @caution: 
*/
static int _LKT_InitDBSConfig (DBS_CONFIG_S *pdbsConfig)
{
    mxml_node_t *tree = NULL;
    int ret           =  ERR_COMMON_FAILED;
        
    /* 加载DBS XML配置文件 */
    tree = _LKT_LoadXML (DBS_SQLRELAY_CONF);
    if (NULL == tree) 
    {
        return ret;
    }
        
    do 
    {
        mxml_node_t *instances = mxmlFindElement(tree, tree, "instances",NULL, NULL,MXML_DESCEND);
        if (NULL == instances)
        {
            dzlog_error("get xml node 'instances' fail");
            break;
        }
        
        mxml_node_t *instance = mxmlFindElement(instances, instances, "instance",NULL, NULL,MXML_DESCEND);
        if (NULL == instances)
        {
            dzlog_error("get xml node 'instance' fail");
            break;
        }
        
        /* 解析 datacenter */
        {
            mxml_node_t *datacenter = mxmlFindElement(instance, instance, "datacenter",NULL, NULL,MXML_DESCEND);
            if (NULL == datacenter)
            {
                dzlog_error("get xml node 'datacenter' fail");
                break;
            }
            
            const char *apiHost = mxmlElementGetAttr(datacenter,"host");
            if (NULL == apiHost)
            {
                dzlog_error("get xml element 'host' fail");
                break;
            }

            char *ptr = strcasestr (apiHost, "https://");
            if (NULL != ptr)
            {
                snprintf (pdbsConfig->host, sizeof (pdbsConfig->host), "%s", ptr + strlen("https://"));
                ghttp_protocol = "https";
            }
            else 
            {
                ptr = strcasestr (apiHost, "http://");
                if (NULL != ptr)
                {
                    snprintf (pdbsConfig->host, sizeof (pdbsConfig->host), "%s", ptr + strlen("http://"));
                    ghttp_protocol = "http";
                }
            }
            dzlog_info("datacenter host:%s, protocol:%s", pdbsConfig->host, ghttp_protocol);
            if (0 == strlen (pdbsConfig->host))
            {
                dzlog_error("get xml element 'host' fail");
                break;
            }

            const char *port = mxmlElementGetAttr(datacenter,"port");
            if (NULL == port)
            {
                dzlog_error("get xml element 'port' fail");
                break;
            }
            pdbsConfig->port = atoi (port);
            dzlog_info("datacenter port:%d", pdbsConfig->port);
        }

        /* 解析 app */
        {
            mxml_node_t *appinfo = mxmlFindElement(instance, instance, "appinfo",NULL, NULL,MXML_DESCEND);
            if (NULL == appinfo)
            {
                dzlog_error("get xml node 'appinfo' fail");
                break;
            }
            
            const char *accessid = mxmlElementGetAttr(appinfo,"accessid");
            if (NULL == accessid)
            {
                dzlog_error("get xml element 'accessid' fail");
                break;
            }
            snprintf (pdbsConfig->accKeyID, sizeof (pdbsConfig->accKeyID), "%s", accessid);
            dzlog_info("accessid:%s", pdbsConfig->accKeyID);
            
            const char *accesssecret = mxmlElementGetAttr(appinfo,"accesssecret");
            if (NULL == accesssecret)
            {
                dzlog_error("get xml element 'accesssecert' fail");
                break;
            }
            snprintf (pdbsConfig->accKeySecret, sizeof (pdbsConfig->accKeySecret), "%s", accesssecret);
            dzlog_info("accesssecret:%s", pdbsConfig->accKeySecret);
        }

        /* 解析dbs   账户 */
        {
            mxml_node_t *auths = mxmlFindElement(instance, instance, "auths",NULL, NULL,MXML_DESCEND);
            if (NULL == auths)
            {
                dzlog_error("get xml node 'auths' fail");
                break;
            }
            
            mxml_node_t *auth = mxmlFindElement(auths, auths, "auth",NULL, NULL,MXML_DESCEND);
            if (NULL == auth)
            {
                dzlog_error("get xml node 'auth' fail");
                break;
            }
            
            mxml_node_t *user = mxmlFindElement(auth, auth, "user",NULL, NULL,MXML_DESCEND);
            if (NULL == user)
            {
                dzlog_error("get xml node 'user' fail");
                break;
            }
            
            /* 解析 dbs user */
            const char *dbsuser = mxmlElementGetAttr(user,"user");
            if (NULL == user)
            {
                dzlog_error("get xml element 'user' fail");
                break;
            }
            snprintf (pdbsConfig->dbsUser, sizeof (pdbsConfig->dbsUser), "%s", dbsuser);
            
            /* 解析 dbs password */
            const char *dbspassword = mxmlElementGetAttr(user,"password");
            if (NULL == dbspassword)
            {
                dzlog_error("get xml element 'password' fail");
                break;
            }
            snprintf (pdbsConfig->dbsPassword, sizeof (pdbsConfig->dbsPassword), "%s", dbspassword);
        }

        /* 解析dbs   host&&port */
        {
            /* 解析 port */
            mxml_node_t *listeners = mxmlFindElement(instance, instance, "listeners",NULL, NULL,MXML_DESCEND);
            if (NULL == listeners)
            {
                dzlog_error("get xml node 'listeners' fail");
                break;
            }
            
            mxml_node_t *listener = mxmlFindElement(listeners, listeners, "listener",NULL, NULL,MXML_DESCEND);
            if (NULL == listener)
            {
                dzlog_error("get xml node 'listener' fail");
                break;
            }
            const char *dbsPort = mxmlElementGetAttr(listener,"port");
            if (NULL == dbsPort)
            {
                dzlog_error("get xml element 'port' fail");
                break;
            }
            pdbsConfig->dbsPort = atoi (dbsPort);
            
            /* 解析 host */
            snprintf (pdbsConfig->dbsHost, sizeof (pdbsConfig->dbsHost), "%s", "127.0.0.1");
        }
                
        ret = ERR_COMMON_SUCCESS;
    } while (0);

    if (ERR_COMMON_SUCCESS != ret)
    {
        dzlog_error( "parse xml tree failed");
    }

    dzlog_debug("DataCenter host[%s] port[%d]", pdbsConfig->host, pdbsConfig->port );

    /* 卸载XML配置文件 */
    _LKT_UnloadXML (tree);

    return ret;
}

/**
* @Function:_LKT_InitCurl
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 初始化libcurl
* @caution: 只需要调用一次，不再使用后，调用_LKT_ReleaseCurl()释放资源;
*/
static int _LKT_InitCurl (void)
{
    CURLcode ret = CURLE_OK;
    
    ret = curl_global_init(CURL_GLOBAL_DEFAULT);
    if (CURLE_OK != ret) 
    {
        dzlog_error( "curl_global_init failed");
        return ERR_COMMON_FAILED;
    }

    return ret;
}

/**
* @Function:_LKT_ReleaseCurl
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 释放libcurl
* @caution: 
*/
static void _LKT_ReleaseCurl (void)
{
    curl_global_cleanup ();
    return;
}

/**
* @Function:_LKT_GetGmtTime
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 获取GMT时间
* @caution: 
*/
static int _LKT_GetGmtTime (char* gmTime)  
{  
    if (gmTime == NULL)  
    {  
        return -1;  
    }  
    
    struct tm* timeInfo = NULL;
    time_t rawTime      = 0;
    
    time(&rawTime);
    timeInfo = gmtime(&rawTime);

    
    char buffer[64] = {0};  
    strftime(buffer,sizeof(buffer),"%d %b %Y %H:%M:%S GMT",timeInfo);
    
    snprintf(gmTime, TIME_LEN, "%s", buffer);  
  
    return strlen(gmTime);
    
}  

/**
* @Function:_LKT_GenerateReqSign
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 生成Request签名
* @caution: 
*/
static int _LKT_GenerateReqSign 
(
    const char* httpVerb, 
    char* date, 
    unsigned char* contendMD5, 
    char* canonicalizedheader,
    char* canonicalizedResource,
    char* sign
)
{   
    if ((NULL == httpVerb) || (NULL == date) || 
        (NULL == contendMD5) || (NULL == canonicalizedheader) ||
        (NULL == canonicalizedResource) || (NULL == sign))
    {
        return ERR_COMMON_INVALID_PARAM;
    }
    
    snprintf (sign, AUTHOR_LEN, "%s\n%s\n%s\n%s\n%s", 
              IDENTIFY_GET, date, contendMD5, canonicalizedheader, canonicalizedResource);

    return ERR_COMMON_SUCCESS;
}

/**
* @Function:_LKT_GenAuthorString
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 生成Author字符串
* @caution: 
*/
static int _LKT_GenAuthorString (char* accKeyID, char* accKeySecret, char* date,  char* authorString, char *api)
{
    int ret = ERR_COMMON_SUCCESS;
    
    if ((NULL == accKeyID) || (NULL == accKeySecret) || (NULL == date) || (NULL == authorString))
    {
        return ERR_COMMON_INVALID_PARAM;
    }

    /* 计算body的MD5(base64) */
    unsigned char base64Out[MD5_BASE64_LEN] = {0};
    {
        unsigned char md5[MD5_BYTE_LEN] = {0};
        ret = LKT_Digest(ALGO_MD5, EMPTY_BODY_STRING, md5);
        if (ERR_COMMON_SUCCESS != ret)
        {
            dzlog_error( "LKT_Digest failed");
            return ret;
        }
        
        /* 转换MD5值为BASE64 */
        int base64Len = 0;
        int newLine   = 1;
        if (NULL == LKT_ConvertToBase64 (md5, sizeof (md5), base64Out, &base64Len, newLine))
        {
            ret = ERR_CONVERT_FAILED;
            dzlog_error( "LKT_ConvertToBase64 failed");
            return ret;
        }
    }

    /* Generate Request Sign */
    char sign[AUTHOR_LEN] = {0};
    {
        char canonicalizedHeader[] = "";
        char *canonicalizedResource = api;
        _LKT_GenerateReqSign (IDENTIFY_GET, date, base64Out, canonicalizedHeader, canonicalizedResource, sign);
        if (ERR_COMMON_SUCCESS != ret)
        {
            dzlog_error( "_LKT_GenerateReqSign failed");
            return ret;
        }
    }
    
    /* 计算hmac (hex) */
    char hmacHex[HMAC_LEN] = {0};
    {
        unsigned char hmacOut[HMAC_LEN] = {0};
        int outlen                      = 0;
        ret = LKT_Hmac (ALGO_SHA1, (unsigned char*) sign, (unsigned char*) accKeySecret, hmacOut, &outlen);
        if (ERR_COMMON_SUCCESS != ret)
        {
            dzlog_error( "LKT_Hmac failed");
            return ret;
        }

        int hmacLen = 0;
        LKT_ConvertToHex (hmacOut, outlen, (unsigned char*) hmacHex, &hmacLen);
        if (ERR_COMMON_SUCCESS != ret)
        {
            dzlog_error( "LKT_ConvertToHex failed");
            return ret;
        }
    }

    /* 组装 */
    snprintf (authorString, AUTHOR_LEN, "%s %s:%s", HEADER_SIGNATURE, accKeyID, hmacHex);
    
    return ERR_COMMON_SUCCESS;
}

/**
* @Function:_LKT_ReciveHTTPData
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: CURL获取数据的回调函数
* @caution: 
*/
static size_t _LKT_ReciveHTTPData(void *input, size_t size, size_t count, void *avg)  
{ 
    if ((NULL == input) || (0 == size) || (0 == count) || (NULL == avg))
    {
        return 0;
    }
    
    size_t reciveLen          = size * count;
    
    RECV_BUFFER_S *recvBuffer = (RECV_BUFFER_S*) avg;
    if (NULL == recvBuffer->content)
    {
        recvBuffer->bufferLen = HTTP_BUFFER_MAX_LEN;
        recvBuffer->content = (char*) malloc (recvBuffer->bufferLen);
        if (NULL == recvBuffer->content)
        {
            dzlog_error("malloc failed, %s", strerror(errno));
            return reciveLen;
        }
    }
    else if ((recvBuffer->writeLen + reciveLen) > recvBuffer->bufferLen)
    {
        recvBuffer->bufferLen += HTTP_BUFFER_MAX_LEN;
        recvBuffer->content = realloc (recvBuffer->content, recvBuffer->bufferLen);
        if (NULL == recvBuffer->content)
        {
            dzlog_error("realloc failed, %s", strerror(errno));
            return reciveLen;
        }
    }
    
    memcpy (recvBuffer->content + recvBuffer->writeLen, input, reciveLen);
    recvBuffer->writeLen += reciveLen;

    return reciveLen;  
}  

/**
* @Function:_LKT_GetAccessKey
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 从JSON中获取AccessKey
* @caution: 
*/
static int _LKT_GetAccessKey(const char *json, char *appKey, char *appSecret)
{
    /*
    {
      "appKey": "e85a380eaa1445f49fc6db7888c1766b",
      "appKeyLevel": 1,
      "appSecret": "068768358aaf4bf8b1472a215ae33d4d",
      "hostDomain": "www.zenzet.com",
      "solutionType": "gateway",
      "status": "DRAFT"
       }
    */
    
    if ((NULL == json) || (NULL == appKey) || (NULL == appSecret))
    {
        return ERR_COMMON_INVALID_PARAM;
    }

    cJSON *root = cJSON_Parse(json);
    if (NULL == root)
    {
        dzlog_error( "cJSON_Parse failed, json:%s", json);
        return ERR_JSON_PARSE_FAILED;
    }

    char *node = NULL;
    cJSON *tmp = NULL;

    node = IDENTIFY_APPKEY;
    tmp = cJSON_GetObjectItem(root, node);
    if (NULL != tmp)
    {
        snprintf (appKey, ACCESSKEY_ID_MAX_LEN, "%s", tmp->valuestring);
    }

    node = IDENTIFY_APPSCRET;
    tmp = cJSON_GetObjectItem(root, node);
    if (NULL != tmp)
    {
        snprintf (appSecret, ACCESSKEY_SECRET_MAX_LEN, "%s", tmp->valuestring);
    }

    /* 资源回收 */
    cJSON_Delete (root);

    return ERR_COMMON_SUCCESS;
}

/**
* @Function:_LKT_Running
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 主线程阻塞
* @caution: 
*/
static void _LKT_Running (void)
{
    while (1)
    {
        sleep (RUNNING_INTERVAL);
    }

    return;
}

/**
* @Function:_LKT_SendHTTPRequest
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 发送HTTP Request
* @caution: 
*/
static int _LKT_SendHTTPRequest 
(
    const char* url, 
    const char *headers[], 
    int headerNum, 
    const char* param,
    long *responseCode,
    char **responseContent
)
{
    (void) param;
    
    if ((NULL == url) || (NULL == responseCode) || (NULL == responseContent))
    {
        dzlog_error("Invalid param");
        return ERR_COMMON_INVALID_PARAM;
    }
    
    int ret                        = ERR_COMMON_SUCCESS;
    
    /* 添加headers */
    int i                          = 0;
    struct curl_slist *curlHeaders = NULL;
    for (i = 0; i < headerNum; i++)
    {
        if (NULL != headers[i])
        {
            curlHeaders = curl_slist_append(curlHeaders, headers[i]);
        }
    }
    
    CURL *handle = curl_easy_init();  
    if (NULL == handle) 
    {   
        dzlog_error( "curl_easy_init failed");
        return ERR_COMMON_FAILED;  
    }

    /* 添加HTTP参数 */
    DBS_S *pdbs = _LKT_GetDBS ();
    
    {
        /* HTTP URL */
        curl_easy_setopt(handle, CURLOPT_URL,url); 

        /* HTTP header */
        curl_easy_setopt(handle, CURLOPT_HTTPHEADER, curlHeaders);

        /* 连接超时 */
        curl_easy_setopt(handle, CURLOPT_CONNECTTIMEOUT , CURL_CONNECT_TIMEOUT);
        
        /* 接收数据超时 (若在CURL_TRANSFER_TIMEOUT时间内，数据仍旧没有接收成功，则超时停止) */
        curl_easy_setopt(handle, CURLOPT_TIMEOUT, CURL_TRANSFER_TIMEOUT);

        /* 下载数据包括HTTP头部 */
        curl_easy_setopt(handle, CURLOPT_HEADER, 0);

        /* 打印详细信息 (可去除) */
        curl_easy_setopt(handle, CURLOPT_VERBOSE, 0);

        #if 1
        /* 不需要验证SSL证书时可以通过如下两个参数设置 */
        curl_easy_setopt(handle, CURLOPT_SSL_VERIFYHOST, 0);
        curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0);
        #endif

        /* 屏蔽超时信号，解决CURL崩溃的问题 */
        curl_easy_setopt(handle, CURLOPT_NOSIGNAL, 1L);

        /* 接收数据的回调函数 */
        curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, &_LKT_ReciveHTTPData);

        /* 设置buffer */
        pdbs->recvBuff.writeLen = 0;
        if (NULL != pdbs->recvBuff.content)
        {
            memset (pdbs->recvBuff.content, 0, pdbs->recvBuff.bufferLen);
        }
        curl_easy_setopt(handle, CURLOPT_WRITEDATA, &pdbs->recvBuff);
    }

    do
    {
        /* 执行HTTP */
        CURLcode curlRet = curl_easy_perform(handle);  
        if (CURLE_OK != curlRet) 
        {
            ret = ERR_CURL_FAILED;
            break;
        }
        *responseContent = pdbs->recvBuff.content;
        
        /* 获取返回码 */
        curlRet = curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE , responseCode);
        if (CURLE_OK != curlRet) 
        {  
            ret = ERR_CURL_FAILED;
            dzlog_error( "curl_easy_getinfo() failed");
            break;
        }
        if (HTTP_OK != *responseCode)
        {
            ret = ERR_API_RESPONSE_FAILED;
            dzlog_error( "responseCode, %ld", *responseCode);
            break;
        }
    } while (0);

    /* 释放curl handle */
    curl_easy_cleanup(handle);

    return ret;
}

/**
* @Function:_LKT_RetrieveDeltaChangesStatus
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 获取增量更新状态
* @caution: 
*/
static int _LKT_RetrieveDeltaChangesStatus (char content[DELTA_STATUS_BUF_LEN])
{
    int static errCount = 0;
    if (NULL == content)
    {
        dzlog_error("Invalid param");
        return ERR_COMMON_INVALID_PARAM;
    }
    
    DBS_S *pdbs              = _LKT_GetDBS ();
    DATA_SHM_S *pdataShm     = pdbs->dataShm;
    
    int ret = ERR_COMMON_SUCCESS;
    
    char lastSyncAt[HTTP_HEADER_LEN] = {0};
    snprintf (lastSyncAt, sizeof (lastSyncAt), "%s=%lld", IDENTIFY_LASTSYNC, pdataShm->lastSyncTime);

    char appKey[HTTP_HEADER_LEN] = {0};
    snprintf (appKey, sizeof (appKey), "%s=%s", IDENTIFY_APPKEY, pdbs->dbsConfig.accKeyID);

    char httpURL[URL_LEN] = {0};
    const char *api = API_LONG_PULL;
    snprintf (httpURL, sizeof (httpURL), 
              "%s://%s:%d%s?%s&%s", HTTP_PROTOCOL, pdbs->dbsConfig.host, pdbs->dbsConfig.port, 
               api, lastSyncAt,appKey);

    /* 添加签名校验 */
    int i = 0;
    const char* headers[3] = {0};
    {
        /* 获取GMT */
        char GMTime[TIME_LEN] = {0};
        (void) _LKT_GetGmtTime (GMTime);

        /* 添加header -> appKey2 */
        char appKey2[HTTP_HEADER_LEN] = {0};
        memset (appKey2, 0, sizeof (appKey2));
        {
            snprintf (appKey2, sizeof (appKey2), "%s: %s", IDENTIFY_APPKEY, pdbs->dbsConfig.accKeyID);
            headers[i++] = appKey2;
        }

        /* 添加header -> data */
        char date[DATE_LEN]  = {0};
        {
            snprintf ((char*) date, sizeof (date), "%s: %s", IDENTIFY_DATE, GMTime);
            headers[i++] = date;
        }

        /* 添加header -> Authorization */
        char author[AUTHOR_LEN] = {0};
        char sign[AUTHOR_LEN] = {0};
        {
            ret = _LKT_GenAuthorString (pdbs->dbsConfig.accKeyID, pdbs->dbsConfig.accKeySecret, GMTime, author, API_LONG_PULL);
            if (ERR_COMMON_SUCCESS != ret)
            {
                dzlog_error( "generate author string failed");
                return ret;
            }

            snprintf (sign, sizeof (sign), "%s: %s", IDENTIFY_AUTHOR, author);
            headers[i++] = sign;
        }
    }

    long responseCode      = 0;
    char *responseContent = NULL;
    ret = _LKT_SendHTTPRequest (httpURL, headers, i, NULL, &responseCode, &responseContent);
    if (ERR_COMMON_SUCCESS != ret)
    {
        if (errCount < SUPPRESS_MAX_ERRORS)
        {
            dzlog_error ("Send HTTP Request failed, url:%s, code:%ld, responseContent:%s", 
                            httpURL, responseCode, responseContent);
            errCount++;
        }
        else if (SUPPRESS_MAX_ERRORS == errCount)
        {
            dzlog_error ("Too many errors, suppress error log");
            errCount++;
        }
        return ret;
    }
    errCount = 0;
    dzlog_debug("Delta changes reponse url:%s, code:%ld, content[%s]",httpURL, responseCode, responseContent);

    snprintf (content, DELTA_STATUS_BUF_LEN, "%s", responseContent);

    return ERR_COMMON_SUCCESS;
}

/**
* @Function:_LKT_HaveMoreData
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 判断是否有更多的数据
* @caution: 
*/
static int _LKT_HaveMoreData (int cursor)
{
    int moreData = 0;
    
    if (cursor > 0)
    {
        moreData = 1;
    }
    
    return moreData;
}

/**
* @Function:_LKT_ParseJSONStrategy
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 解析JSON中的一条策略
* @caution:
*/
static int _LKT_ParseJSONStrategy (cJSON *top, STRATEGY_S *pstrategy)
{
    if ((NULL == top) || (NULL == pstrategy))
    {
        dzlog_error("Invalid param");
        return ERR_COMMON_INVALID_PARAM;
    }
    
    int ret = ERR_COMMON_SUCCESS;

    char *node    = NULL;
    cJSON *tmp    = NULL;

    do 
    {
        node = IDENTIFY_STRATEGYID;
        tmp = cJSON_GetObjectItem(top, node);
        if (NULL == tmp)
        {
            dzlog_error("can't find %s", node);
            ret = ERR_INVALID_STRATEGY;
            break;
        }
        else
        {
            pstrategy->strategyID = tmp->valueint;
        }
        
        node = IDENTIFY_COLUMNNAME;
        tmp = cJSON_GetObjectItem(top, node);
        if (NULL == tmp)
        {
            dzlog_error("can't find %s", node);
            ret = ERR_INVALID_STRATEGY;
            break;
        }
        else
        {
            snprintf (pstrategy->colName, sizeof (pstrategy->colName), "%s", tmp->valuestring);
        }
        
        node = IDENTIFY_TABLENAME;
        tmp = cJSON_GetObjectItem(top, node);
        if (NULL == tmp)
        {
            dzlog_error("can't find %s", node);
            ret = ERR_INVALID_STRATEGY;
            break;
        }
        else
        {
            snprintf (pstrategy->tblName, sizeof (pstrategy->tblName), "%s", tmp->valuestring);
        }
        
        node = IDENTIFY_DBNAME;
        tmp = cJSON_GetObjectItem(top, node);
        if (NULL == tmp)
        {
            dzlog_error("can't find %s", node);
            ret = ERR_INVALID_STRATEGY;
            break;
        }
        else
        {
            snprintf (pstrategy->dbName, sizeof (pstrategy->dbName), "%s", tmp->valuestring);
        }
    }while (0);
        
    if (ERR_COMMON_SUCCESS != ret)
    {
        return ret;
    }

    do
    {
        node = IDENTIFY_STATE;
        tmp = cJSON_GetObjectItem(top, node);
        if (NULL == tmp)
        {
            dzlog_error("can't find %s", node);
            ret = ERR_INVALID_STRATEGY;
            break;
        }
        else
        {
            pstrategy->state = tmp->valueint;
        }

        node = IDENTIFY_DATATYPE;
        tmp = cJSON_GetObjectItem(top, node);
        if (NULL == tmp)
        {
            dzlog_error("can't find %s", node);
            ret = ERR_INVALID_STRATEGY;
            break;
        }
        else
        {
            snprintf(pstrategy->datatype, sizeof(pstrategy->datatype), "%s", tmp->valuestring);
        }

        node = IDENTIFY_SEARCHABLE;
        tmp = cJSON_GetObjectItem(top, node);
        if (NULL != tmp)
        {
            pstrategy->searchable = tmp->valueint;
        }
        else
        {
            pstrategy->searchable = 0;
        }

        node = IDENTIFY_CHARSET;
        tmp = cJSON_GetObjectItem(top, node);
        if (NULL != tmp)
        {
            snprintf(pstrategy->charset, sizeof(pstrategy->charset), "%s", tmp->valuestring);
        }
        else
        {
            snprintf(pstrategy->charset, sizeof(pstrategy->charset), "%s", IDENTIFY_DEFAULT_CHARSET);
        }

    }while (0);
        
    if (ERR_COMMON_SUCCESS != ret)
    {
        return ret;
    }

    do
    {
        cJSON *pdataKeyTop   = NULL;
        DATA_KEY_S *pdataKey = &pstrategy->datakey;
        
        node = IDENTIFY_DATAKEY;
        pdataKeyTop = cJSON_GetObjectItem(top, node);
        if (NULL == pdataKey)
        {
            dzlog_error("can't find %s", node);
            ret = ERR_INVALID_STRATEGY;
            break;
        }

        node = IDENTIFY_DATAKEY_KEY;
        tmp = cJSON_GetObjectItem(pdataKeyTop, node);
        if (NULL == tmp)
        {
            dzlog_error("can't find %s", node);
            ret = ERR_INVALID_STRATEGY;
            break;
        }
        else
        {
            snprintf (pdataKey->key, sizeof (pdataKey->key), "%s", tmp->valuestring);
        }
        
        node = IDENTIFY_DATAKEY_IV;
        tmp = cJSON_GetObjectItem(pdataKeyTop, node);
        if (NULL == tmp)
        {
            dzlog_error("can't find %s", node);
            ret = ERR_INVALID_STRATEGY;
            break;
        }
        else
        {
            snprintf (pdataKey->iv, sizeof (pdataKey->iv), "%s", tmp->valuestring);
        }
        
        node = IDENTIFY_DATAKEY_MAPPINGID;
        tmp = cJSON_GetObjectItem(pdataKeyTop, node);
        if (NULL == tmp)
        {
            dzlog_error("can't find %s", node);
            ret = ERR_INVALID_STRATEGY;
            break;
        }
        else
        {
            pdataKey->mappingid = tmp->valueint;
        }
        
        /*
        node = IDENTIFY_DATAKEY_DISABLEDECRYPT;
        tmp = cJSON_GetObjectItem(pdataKeyTop, node);
        if (NULL == tmp)
        {
            dzlog_error("can't find %s", node);
            ret = ERR_INVALID_STRATEGY;
            break;
        }
        else
        {           
        }
        */
        
        node = IDENTIFY_DATAKEY_ENABLEENCRYPT;
        tmp = cJSON_GetObjectItem(pdataKeyTop, node);
        if (NULL == tmp)
        {
            dzlog_error("can't find %s", node);
            ret = ERR_INVALID_STRATEGY;
            break;
        }
        else
        {
            pdataKey->enableEncrypt = tmp->valueint;            
        }
    }while (0);
    
    if (ERR_COMMON_SUCCESS != ret)
    {
        return ret;
    }

    do 
    {
        cJSON *pcipherTop = NULL;
        CIPHER_S *pcipher = &pstrategy->cipher;
         
        node = IDENTIFY_CIPHER;
        pcipherTop = cJSON_GetObjectItem(top, node);
        if (NULL == pcipherTop)
        {
            dzlog_error("can't find %s", node);
            ret = ERR_INVALID_STRATEGY;
            break;
        }
         
        node = IDENTIFY_CIPHER_ID;
        tmp = cJSON_GetObjectItem(pcipherTop, node);
        if (NULL == pcipherTop)
        {
            dzlog_error("can't find %s", node);
            ret = ERR_INVALID_STRATEGY;
            break;
        }
        else
        {
            pcipher->id = tmp->valueint;
        }
        
        node = IDENTIFY_CIPHER_ALGO;
        tmp = cJSON_GetObjectItem(pcipherTop, node);
        if (NULL == pcipherTop)
        {
            dzlog_error("can't find %s", node);
            ret = ERR_INVALID_STRATEGY;
            break;
        }
        else
        {
            snprintf (pcipher->algo, sizeof (pcipher->algo), "%s", tmp->valuestring);
        }
        
        node = IDENTIFY_CIPHER_MODE;
        tmp = cJSON_GetObjectItem(pcipherTop, node);
        if (NULL == pcipherTop)
        {
            dzlog_error("can't find %s", node);
            ret = ERR_INVALID_STRATEGY;
            break;
        }
        else
        {
            snprintf (pcipher->mode, sizeof (pcipher->mode), "%s", tmp->valuestring);
        }
        
        node = IDENTIFY_CIPHER_PADDING;
        tmp = cJSON_GetObjectItem(pcipherTop, node);
        if (NULL == pcipherTop)
        {
            dzlog_error("can't find %s", node);
            ret = ERR_INVALID_STRATEGY;
            break;
        }
        else
        {
            snprintf (pcipher->padding, sizeof (pcipher->padding), "%s", tmp->valuestring);
        }
        
        node = IDENTIFY_CIPHER_KEY_LEN;
        tmp = cJSON_GetObjectItem(pcipherTop, node);
        if (NULL == pcipherTop)
        {
            dzlog_error("can't find %s", node);
            ret = ERR_INVALID_STRATEGY;
            break;
        }
        else
        {
            pcipher->keyLen = tmp->valueint;
        }
        
        node = IDENTIFY_CIPHER_IV_LEN;
        tmp = cJSON_GetObjectItem(pcipherTop, node);
        if (NULL == pcipherTop)
        {
            dzlog_error("can't find %s", node);
            ret = ERR_INVALID_STRATEGY;
            break;
        }
        else
        {
            pcipher->ivLen = tmp->valueint;
        }
    }while (0);
    
    if (ERR_COMMON_SUCCESS != ret)
    {
        return ret;
    }

    return  ERR_COMMON_SUCCESS;
}

/**
* @Function:_LKT_ParseJSONDelta
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 解析增量更新数据，用完后需要调用_LKT_ReleaseDelta释放内存
* @caution:
*/
static int _LKT_ParseJSONDelta (const char* json, DELTA_S *pdelta, int parseFile)
{
    if ((NULL == json) || (NULL == pdelta))
    {
        dzlog_error("Invalid param");
        return ERR_COMMON_INVALID_PARAM;
    }
    
    int ret = ERR_COMMON_SUCCESS;
    
    cJSON *root = cJSON_Parse(json);
    if (NULL == root)
    {
        dzlog_error( "cJSON_Parse failed");
        return ERR_JSON_PARSE_FAILED;
    }

    do 
    {
        cJSON *tmp = NULL;
        char *node = NULL;
        
        if (0 == parseFile)
        {
            /* 优先判断错误码 */
            node = IDENTIFY_CODE;
            tmp = cJSON_GetObjectItem(root, node);
            if (NULL == tmp)
            {
                dzlog_error("can't find %s", node);
                ret = ERR_INVALID_STRATEGY;
                break; 
            }

            /* 获取code */
            pdelta->code = atoi(tmp->valuestring);
            if (0 != pdelta->code)
            {
                dzlog_error("code:%d", pdelta->code);
                ret = ERR_INVALID_STRATEGY;
                break;
            }
            
            /* 获取cursor */
            node = IDENTIFY_CURSOR;
            tmp = cJSON_GetObjectItem(root, node);
            if (NULL == tmp)
            {
                dzlog_error("can't find %s", node);
                ret = ERR_INVALID_STRATEGY;
                break; 
            }
            pdelta->cursor = tmp->valueint;
        }
        else if (1 == parseFile)
        {
            node = IDENTIFY_APPKEY;
            tmp = cJSON_GetObjectItem(root, node);
            if (NULL == tmp)
            {
                dzlog_error("can't find %s", node);
                ret = ERR_INVALID_APPKEY;
                break; 
            }
            snprintf (pdelta->appKey, sizeof (pdelta->appKey), "%s", tmp->valuestring);
        }
        
        /* 获取最后更新的时间 */
        node = IDENTIFY_LASTSYNC;
        tmp = cJSON_GetObjectItem(root, node);
        if (NULL != tmp)
        {
            if (tmp->valuedouble > 0)
            {
                /* 正常情况下, 更新时间大小，超过32位的int */
                pdelta->lastSync = (long long) tmp->valuedouble;
            }
            else if (tmp->valueint > 0)
            {
                pdelta->lastSync = (long long) tmp->valueint;
            }
        }
        
        node = IDENTIFY_DATA;
        tmp = cJSON_GetObjectItem(root, node);
        if (NULL == tmp)
        {
            dzlog_error("can't find %s", node);
            ret = ERR_INVALID_STRATEGY;
            break; 
        }
        
        node = IDENTIFY_DBS_STRATEGY;
        cJSON *strategies = cJSON_GetObjectItem(tmp, node);
        if (NULL == strategies)
        {
            dzlog_error("can't find %s", node);
            ret = ERR_INVALID_STRATEGY;
            break; 
        }
        
        /* 获取strategy数量 */
        int strategyNum = cJSON_GetArraySize (strategies);
        if (0 == strategyNum)
        {
            dzlog_error("can't find strategy, num:%d", strategyNum);
            ret = ERR_INVALID_STRATEGY;
            break; 
        }
        pdelta->strategyNum = strategyNum;

        pdelta->strategies = (STRATEGY_S*) malloc (sizeof (STRATEGY_S) * pdelta->strategyNum);
        if (NULL == pdelta->strategies)
        {
            ret = ERR_COMMON_NO_MEM;
            dzlog_error("no memory");
            break;
        }
        memset (pdelta->strategies, 0, sizeof (sizeof (STRATEGY_S) * pdelta->strategyNum));

        /* 解析每一个strategy */
        int strategyIdx = 0;
        cJSON *strategy = NULL;

        for (strategyIdx = 0; strategyIdx < pdelta->strategyNum; strategyIdx++)
        {
            strategy = cJSON_GetArrayItem (strategies, strategyIdx);
            if (NULL == strategy)
            {
                dzlog_error("parse error");
                ret = ERR_INVALID_STRATEGY;
                break;
            }

            ret = _LKT_ParseJSONStrategy (strategy, &pdelta->strategies[strategyIdx]);
            if (ERR_COMMON_SUCCESS != ret)
            {
                dzlog_error("parse strategy error");
                break;
            }
        }
    } while (0);

    if (ERR_COMMON_SUCCESS != ret)
    {
        free (pdelta->strategies);
        pdelta->strategies = NULL;
    }
        
    /* 释放资源 */
    cJSON_Delete (root);

    return ret;
}

/**
* @Function:_LKT_ReleaseJsonDelta
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 在_LKT_ParseDelta使用后，需要调用_LKT_ReleaseDelta释放内存
* @caution:
*/
static void _LKT_ReleaseJsonDelta (DELTA_S *pdelta)
{
    if (NULL != pdelta)
    {
        free (pdelta->strategies);
    }
    return;
}

/**
* @Function:_LKT_GetDeltaAndUpdateStrategyToSHM
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 获取增量更新，并刷新到共享内存中
* @caution: 
*/
static int _LKT_GetDeltaAndUpdateStrategyToSHM (void)
{    
    DBS_S *pdbs              = _LKT_GetDBS ();
    DATA_SHM_S *pdataShm     = pdbs->dataShm;
    
    int ret = ERR_COMMON_SUCCESS;
    
    char lastSyncAt[HTTP_HEADER_LEN] = {0};
    snprintf (lastSyncAt, sizeof (lastSyncAt), "%s=%lld", IDENTIFY_LASTSYNC, pdataShm->lastSyncTime);

    char appKey[HTTP_HEADER_LEN] = {0};
    snprintf (appKey, sizeof (appKey), "%s=%s", IDENTIFY_APPKEY, pdbs->dbsConfig.accKeyID);
    
    char maxCount[HTTP_HEADER_LEN] = {0};
    snprintf (maxCount, sizeof (maxCount), "%s=%d", IDENTIFY_MAXCOUNT, QUERY_PAGE_COUNT);
    
    int cursor = 0;
    char url[URL_LEN]       = {0};
    char *api               = API_DBS_STRATEGY;
    long responseCode       = 0;
    char *responseContent   = NULL;
    DELTA_S delta;

    while (1)
    {
        // maxCount和cursor可选,不传的话默认20和0
        snprintf (url, sizeof (url), 
                  "%s://%s:%d%s?%s&%s&%s&%s=%d", HTTP_PROTOCOL, pdbs->dbsConfig.host, pdbs->dbsConfig.port, 
                   api, lastSyncAt,appKey,maxCount,IDENTIFY_CURSOR,cursor);
    
	    dzlog_debug("update strategy request[%s]",url);	

        /* 添加签名校验 */
        int i = 0;
        const char* headers[3] = {0};
        {
            /* 获取GMT */
            char GMTime[TIME_LEN] = {0};
            (void) _LKT_GetGmtTime (GMTime);

            /* 添加header -> appKey2 */
            char appKey2[HTTP_HEADER_LEN] = {0};
            memset (appKey2, 0, sizeof (appKey2));
            {
                snprintf (appKey2, sizeof (appKey2), "%s: %s", IDENTIFY_APPKEY, pdbs->dbsConfig.accKeyID);
                headers[i++] = appKey2;
            }

            /* 添加header -> data */
            char date[DATE_LEN]  = {0};
            {
                snprintf ((char*) date, sizeof (date), "%s: %s", IDENTIFY_DATE, GMTime);
                headers[i++] = date;
            }

            /* 添加header -> Authorization */
            char author[AUTHOR_LEN] = {0};
            char sign[AUTHOR_LEN] = {0};
            {
                ret = _LKT_GenAuthorString (pdbs->dbsConfig.accKeyID, pdbs->dbsConfig.accKeySecret, GMTime, author, api);
                if (ERR_COMMON_SUCCESS != ret)
                {
                    dzlog_error( "generate author string failed");
                    return ret;
                }

                snprintf (sign, sizeof (sign), "%s: %s", IDENTIFY_AUTHOR, author);
                headers[i++] = sign;
            }
        }
    
        ret = _LKT_SendHTTPRequest (url, headers, i, NULL, &responseCode, &responseContent);        
        if (ERR_COMMON_SUCCESS != ret)
        {
            dzlog_error ("Send HTTP Request failed, url:%s, code:%ld, responseContent:%s", 
                            url, responseCode, responseContent);
            return ret;
        }

        dzlog_info("update strategy response url:%s, code:%ld, content[%s]", url, responseCode, responseContent); 

        memset (&delta, 0, sizeof (delta));
        ret = _LKT_ParseJSONDelta (responseContent, &delta, 0);
        if (ERR_COMMON_SUCCESS != ret)
        {
            dzlog_error ("Parse delta failed");
            break;
        }
        
        /* 更新共享内存 */
        ret = _LKT_UpdateStrategyToSHM (&delta);
        if (ERR_COMMON_SUCCESS != ret)
        {
            _LKT_ReleaseJsonDelta (&delta);
            dzlog_error ("Refresh strategy to shared memory failed");
            break;
        }
        
        _LKT_ReleaseJsonDelta (&delta);
        
        if (0 == _LKT_HaveMoreData (delta.cursor))
        {
            break;
        }

        cursor = delta.cursor;
    }

    return ERR_COMMON_SUCCESS;
}

/**
* @Function:_LKT_HasDelta
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 是否需要更新
* @caution: 
*/
static int _LKT_HasDelta (void)
{
    int ret     = ERR_COMMON_SUCCESS;
    int change  = 0;
    
    char deltaStatus[DELTA_STATUS_BUF_LEN] = {0};
    ret = _LKT_RetrieveDeltaChangesStatus (deltaStatus);
    if (ERR_COMMON_SUCCESS != ret)
    {
        return change;
    }

    cJSON *root = cJSON_Parse(deltaStatus);
    if (NULL == root)
    {
        dzlog_error( "cJSON_Parse failed, json:%s", deltaStatus);
        return change;
    }

    char *node  = NULL;
    cJSON *tmp  = NULL;
    
    int code   = 0;
    do 
    {
        node = IDENTIFY_CODE;
        tmp = cJSON_GetObjectItem(root, node);
        if (NULL == tmp)
        {
            dzlog_error("can't find %s, json:%s", node, deltaStatus);
            ret = ERR_JSON_PARSE_FAILED;
            break; 
        }
        code = tmp->valueint;

        if (0 != code)
        {
            change = 0;
            break;
        }
        
        node = IDENTIFY_DATA;
        cJSON *data = cJSON_GetObjectItem(root, node);
        if (NULL == data)
        {
            dzlog_error("can't find %s, json:%s", node, deltaStatus);
            ret = ERR_JSON_PARSE_FAILED;
            break; 
        }
        
        node = IDENTIFY_DBS_STRATEGY_CHANGES;
        tmp = cJSON_GetObjectItem(data, node);
        if (NULL == tmp)
        {
            dzlog_error("can't find %s, json:%s", node, deltaStatus);		
            ret = ERR_JSON_PARSE_FAILED;
            break; 
        }

        change = tmp->valueint;
    } while (0);
    
    if (0 != code)
    {
        dzlog_debug ("error code:%d", code);
    }

    /* 释放资源 */
    cJSON_Delete (root);
    
    return change;
}

/**
* @Function:_LKT_CreateSharedMemory
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 创建共享内存
* @caution: 
*/
static int _LKT_CreateSharedMemory ()
{
    int ret      = ERR_COMMON_SUCCESS;
    int shm_id   = 0;
    key_t shmkey = SHM_KEY;
    void* ptr    = (void*) 0;
    
    shm_id = shmget(shmkey, sizeof(DATA_SHM_S), 0666 | IPC_CREAT);
    if (shm_id == -1)
    {
        dzlog_error("shmget failed. %s", strerror(errno));
        return ERR_COMMON_SYSFAILED;
    }
    
    ptr = shmat(shm_id, (void*) 0, 0);
    if ((void*)-1 == ptr)
    {
        dzlog_error("shmat failed. %s", strerror(errno));
        return ERR_COMMON_SYSFAILED;
    }
    
    DBS_S *pdbs   = _LKT_GetDBS ();
    pdbs->dataShm = ptr;

    /* 进程结束后，共享内存仍然会存在，应该清零一次 */
    memset (pdbs->dataShm, 0, sizeof (DATA_SHM_S));

    return ret;
}

/**
* @Function:_LKT_ReleaseSharedMemory
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 释放共享内存
* @caution: 
*/
static void _LKT_ReleaseSharedMemory ()
{
    DBS_S *pdbs   = _LKT_GetDBS ();

    if (shmdt(pdbs->dataShm) == -1)
    {
        dzlog_error("shmdt failed. %s", strerror(errno));
    }

    return;
}

/**
* @Function:_LKT_CreateWRLock
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 创建读写锁，用于共享内存的数据同步
* @caution: 
*/
static int _LKT_CreateWRLock ()
{
    int ret      = ERR_COMMON_SUCCESS;
    DBS_S *pdbs  = _LKT_GetDBS ();
    
    pthread_rwlockattr_t rwLockAttr;
    
    if (ERR_COMMON_SUCCESS != pthread_rwlockattr_init(&rwLockAttr))
    {
        dzlog_error("pthread_rwlockattr_init failed. %s", strerror(errno));
        return ERR_COMMON_SYSFAILED;
    }
    
    if(ERR_COMMON_SUCCESS != pthread_rwlockattr_setpshared(&rwLockAttr,PTHREAD_PROCESS_SHARED))
    {
        dzlog_error("pthread_rwlockattr_setpshared failed. %s", strerror(errno));
        return ERR_COMMON_SYSFAILED;
    }
    
    if(ERR_COMMON_SUCCESS  != pthread_rwlock_init(&pdbs->dataShm->rwLock,&rwLockAttr))
    {
        dzlog_error("pthread_rwlock_init failed. %s", strerror(errno));
        return ERR_COMMON_SYSFAILED;
    }

    if(ERR_COMMON_SUCCESS != pthread_rwlockattr_destroy(&rwLockAttr))
    {
        dzlog_error("pthread_rwlockattr_destroy failed. %s", strerror(errno));
        return ERR_COMMON_SYSFAILED;
    }
    
    return ret;
}

/**
* @Function:_LKT_CheckLocalCacheValidate
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 校验本地的缓存文件对应的APPKEY和dbs.conf配置的是否相同
* @caution: 
*/
static int  _LKT_CheckLocalCacheValidate (char *appKey)
{
    int validate             = 0;
    DBS_S *pdbs              = _LKT_GetDBS ();
    DBS_CONFIG_S *pdbsConfig = &pdbs->dbsConfig;


    if (0 == strncmp (pdbsConfig->accKeyID, appKey, sizeof (pdbsConfig->accKeyID)))
    {
        validate = 1;
    }

    return validate;
}
/**
* @Function:_LKT_UpdateStrategyToSHMFromFile
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 从本地文件加载策略，更新到SHM
* @caution: 
*/
static int _LKT_UpdateStrategyToSHMFromFile (const char* strategyFile)
{
    if (NULL == strategyFile)
    {
        dzlog_error("Invalid param");
        return ERR_COMMON_INVALID_PARAM;
    }
    
    int ret = ERR_COMMON_SUCCESS;
    
    FILE *fp = fopen (strategyFile, "rb+");
    if (NULL == fp)
    {
        dzlog_error("open file[%s] failed. %s", strategyFile, strerror(errno));
        return ERR_COMMON_SYSFAILED;
    }

    char *json = NULL;
    DELTA_S delta;
    
    do
    {
        unsigned long long fileSize = 0;
        
        /* 获取文件大小 */
        struct stat statbuf;  
        if(stat(strategyFile, &statbuf) < 0)
        {  
            dzlog_error("stat %s failed. %s", strategyFile, strerror(errno));
            ret = ERR_COMMON_SYSFAILED;
            break;
        }
        else
        {  
            fileSize = statbuf.st_size;  
        }

        if (0 == fileSize)
        {
            ret = ERR_COMMON_EMPTY_FILE;
            break;
        }

        json = (char*) malloc (fileSize);
        if (NULL == json)
        {
            dzlog_error("stat file[%s] failed. %s", strategyFile, strerror(errno));
            ret = ERR_COMMON_NO_MEM;
            break;
        }
        memset (json, 0, fileSize);

        /* 读取文件内容 */
        int readLen = 0;
        readLen = fread(json, 1, fileSize, fp);
        if (readLen != fileSize)
        {
            dzlog_error("read file[%s] failed. %s", strategyFile, strerror(errno));
            ret = ERR_COMMON_SYSFAILED;
            break;
        }
        else if (0 == readLen)
        {
            dzlog_error("read file[%s] failed. %s", strategyFile, strerror(errno));
            ret = ERR_COMMON_SYSFAILED;
            break;
        }

        /* 解析JSON */
        memset (&delta, 0, sizeof (delta));
        ret = _LKT_ParseJSONDelta (json, &delta, 1);
        if (ERR_COMMON_SUCCESS != ret)
        {
            dzlog_error ("Parse delta failed");
            break;
        }

        int validate = _LKT_CheckLocalCacheValidate (delta.appKey);
        if (0 == validate)
        {
            dzlog_error ("Local cache is not validate");
            break;
        }
        
        /* 更新共享内存 */
        ret = _LKT_UpdateStrategyToSHM (&delta);
        if (ERR_COMMON_SUCCESS != ret)
        {
            dzlog_error ("Refresh strategy to shared memory failed");
            break;
        }
    } while (0);
    
    _LKT_ReleaseJsonDelta (&delta);
    free (json);
    fclose (fp);
    
    return ret;
}

/**
* @Function:_LKT_ReleaseWRLock
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 释放读写锁
* @caution: 
*/
static void _LKT_ReleaseWRLock ()
{
    DBS_S *pdbs  = _LKT_GetDBS ();
    
    if(ERR_COMMON_SUCCESS != pthread_rwlock_destroy(&pdbs->dataShm->rwLock))
    {
        dzlog_error("pthread_rwlock_destroy failed. %s", strerror(errno));
        return;
    }

    return;
}

/**
* @Function:_LKT_IsStrategyExist
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 判断策略是否已经存在
* @caution: 
*/
static int _LKT_IsStrategyExist 
(
    const char* colName, 
    const char* tblName,
    const char* dbName,
    int *colIdx
)
{    
    int exist = 1;
    
    DBS_S *pdbs         = _LKT_GetDBS ();
    APP_INFO_S *papp    = &pdbs->dataShm->appInfo;

    int i              = 0;
    int dstColLen      = 0;
    int srcColLen      = strlen(colName);
    int maxLen         = 0;
    COLUMN_STRATEGY_S *colStrategyList = papp->columnStrategyList;
    TABLE_STRATEGY_S  *tblStrategyList = papp->tblStrategyList;
    DB_STRATEGY_S     *dbStrategyList  = papp->dbStrategyList;
    
    for (i = 0; i < papp->columnNum; i++)
    {
        srcColLen = strlen(colName);
        dstColLen = strlen(colStrategyList[i].columnName);
        maxLen = srcColLen > dstColLen ? srcColLen : dstColLen;
        
        if (0 != strncmp (colName, colStrategyList[i].columnName, maxLen))
        {
            continue;
        }

        int tblIdx = colStrategyList[i].tblIdx;
        srcColLen  = strlen(tblName);
        dstColLen = strlen(tblStrategyList[tblIdx].tableName);
        maxLen = srcColLen > dstColLen ? srcColLen : dstColLen;
        if (0 != strncmp (tblName, tblStrategyList[tblIdx].tableName, maxLen))
        {
            continue;
        }
        
        int dbIdx = colStrategyList[i].dbIdx;
        srcColLen  = strlen(dbName);
        dstColLen = strlen(dbStrategyList[dbIdx].dbName);
        maxLen = srcColLen > dstColLen ? srcColLen : dstColLen;
        if (0 != strncmp (dbName, dbStrategyList[dbIdx].dbName, maxLen))
        {
            continue;
        }

        *colIdx = i;
        break;
    }

    if (papp->columnNum == i)
    {
        exist = 0;
    }

    return exist;
}

/**
* @Function:_LKT_IsTblExistInStrategy
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 判断数据表是否存在于策略中
* @caution: 
*/
static int _LKT_IsTblExistInStrategy (const char* tblName, int *idx)
{
    int exist = 0;

    if ((NULL == tblName) || (NULL == idx))
    {
        return exist;
    }
    
    DBS_S *pdbs         = _LKT_GetDBS ();
    DATA_SHM_S *shm     = pdbs->dataShm;

    int i              = 0;
    int strategyTblLen = 0;
    int inTblLen       = strlen(tblName);
    int maxLen         = 0;
    int tblNum         = shm->appInfo.tableNum;
    TABLE_STRATEGY_S *tblStrateyList = shm->appInfo.tblStrategyList;
    
    for (i = 0; i < tblNum; i++)
    {
        strategyTblLen = strlen(tblStrateyList[i].tableName);
        maxLen = inTblLen > strategyTblLen ? inTblLen : strategyTblLen ;
        
        if (0 == strncmp (tblName, tblStrateyList[i].tableName, maxLen))
        {
            exist = 1;
            *idx = i;
            break;
        }
    }

    return exist;
}

/**
* @Function:_LKT_IsDBExistInStrategy
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 判断数据库是否存在于策略中
* @caution: 
*/
static int _LKT_IsDBExistInStrategy (const char* dbName,  int *idx)
{
    int exist = 0;
    
    if ((NULL == dbName) || (NULL == idx))
    {
        return exist;
    }
    
    DBS_S *pdbs         = _LKT_GetDBS ();
    DATA_SHM_S *pshm     = pdbs->dataShm;

    int i             = 0;
    int strategyDBLen = 0;
    int inDBLen       = strlen(dbName);
    int maxLen        = 0;
    int dbNum         = pshm->appInfo.dbNum;
    DB_STRATEGY_S *dbStrateyList = pshm->appInfo.dbStrategyList;
    
    for (i = 0; i < dbNum; i++)
    {
        strategyDBLen = strlen(dbStrateyList[i].dbName);
        maxLen = inDBLen > strategyDBLen ? inDBLen : strategyDBLen;
        
        if (0 == strncmp (dbName, dbStrateyList[i].dbName, maxLen))
        {
            exist = 1;
            *idx = i;
            break;
        }
    }

    return exist;
}

/**
* @Function:_LKT_InsertStrategy
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 插入新策略到SHM
* @caution: 
*/
static int _LKT_InsertStrategy (STRATEGY_S *strategy)
{
    int ret = ERR_COMMON_SUCCESS;
    
    DBS_S *pdbs         = _LKT_GetDBS ();
    APP_INFO_S *papp    = &pdbs->dataShm->appInfo;

    COLUMN_STRATEGY_S *colStrategyList = papp->columnStrategyList;
    TABLE_STRATEGY_S  *tblStrategyList = papp->tblStrategyList;
    DB_STRATEGY_S     *dbStrategyList  = papp->dbStrategyList;

    if (COLUMN_STRATEGY_MAX_NUM == papp->columnNum)
    {
        dzlog_error ("Too many colunm strategy");
        return ERR_TOO_MANY_STRATEGY;
    }
    
    do
    {
        //dzlog_debug("column[%s] insert in strategy at columnIdx[%d]",strategy->colName, papp->columnNum);
        int insertColIdx    = papp->columnNum;
        int insertTblIdx    = papp->tableNum;
        int insertDbIdx     = papp->dbNum;
        int exist           = 0;
        int tblIdx          = 0;
        int dbIdx           = 0;
        
        exist = _LKT_IsTblExistInStrategy (strategy->tblName, &tblIdx);
        if (1 == exist)
        {
            //dzlog_debug("table[%s] exist in strategy at tableIdx[%d]",strategy->tblName, tblIdx);
            colStrategyList[insertColIdx].tblIdx = tblIdx;
        }
        else
        {
            if (insertTblIdx >= TBL_STRATEGY_MAX_NUM)
            {
                dzlog_error ("Too many table strategy");
                ret = ERR_TOO_MANY_STRATEGY;
                break;
            }

            snprintf (tblStrategyList[insertTblIdx].tableName, TABLE_NAME_MAX_LEN, "%s", strategy->tblName);
            colStrategyList[insertColIdx].tblIdx = papp->tableNum;

            papp->tableNum++;
            tblStrategyList[insertTblIdx].used = 1;

            //dzlog_debug("table[%s] insert in strategy at tableIdx[%d]",strategy->tblName, papp->tableNum);
        }
        
        exist = _LKT_IsDBExistInStrategy (strategy->dbName, &dbIdx);
        if (1 == exist)
        {
            //dzlog_debug("db[%s] exist in strategy at dbIdx[%d]",strategy->dbName, dbIdx);
            colStrategyList[insertColIdx].dbIdx = dbIdx;
        }
        else
        {
            if (insertDbIdx >= DB_STRATEGY_MAX_NUM)
            {
                dzlog_error ("Too many db strategy");
                ret = ERR_TOO_MANY_STRATEGY;
                break;
            }
            snprintf (dbStrategyList[insertDbIdx].dbName, DATABASE_NAME_MAX_LEN, "%s", strategy->dbName);
            colStrategyList[insertColIdx].dbIdx = papp->dbNum;

            //dzlog_debug("db[%s] insert in strategy at dbIdx[%d]",strategy->dbName, papp->dbNum);
            papp->dbNum++;
            dbStrategyList[insertDbIdx].used   = 1;
        }
        
        memcpy (&colStrategyList[insertColIdx].dataKey, &strategy->datakey,sizeof (DATA_KEY_S));
        memcpy (&colStrategyList[insertColIdx].cipher, &strategy->cipher,sizeof (CIPHER_S));
        snprintf (colStrategyList[insertColIdx].columnName, COLUMN_NAME_MAX_LEN, "%s", strategy->colName);
        colStrategyList[insertColIdx].state = strategy->state;
        colStrategyList[insertColIdx].searchable = strategy->searchable;
        snprintf (colStrategyList[insertColIdx].charset, sizeof (colStrategyList[insertColIdx].charset), 
                 "%s", strategy->charset);
        strcpy(colStrategyList[insertColIdx].type, strategy->datatype);
        colStrategyList[insertColIdx].strategyID = strategy->strategyID;
        colStrategyList[insertColIdx].used  = 1;
        papp->columnNum++;
    } while (0);


    return ret;
}

/**
* @Function:_LKT_UpdateStrategy
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 更新SHM中的策略
* @caution: 
*/
static void _LKT_UpdateStrategy (STRATEGY_S *strategy, int colIdx)
{
    DBS_S *pdbs         = _LKT_GetDBS ();
    APP_INFO_S *papp    = &pdbs->dataShm->appInfo;

    COLUMN_STRATEGY_S *colStrategyList = papp->columnStrategyList;

    memcpy (&colStrategyList[colIdx].dataKey, &strategy->datakey,sizeof (DATA_KEY_S));
    memcpy (&colStrategyList[colIdx].cipher, &strategy->cipher,sizeof (CIPHER_S));
    colStrategyList[colIdx].state = strategy->state;
    colStrategyList[colIdx].searchable = strategy->searchable;
    snprintf (colStrategyList[colIdx].charset, sizeof (colStrategyList[colIdx].charset), 
             "%s", strategy->charset);
    strcpy(colStrategyList[colIdx].type, strategy->datatype);
    colStrategyList[colIdx].strategyID = strategy->strategyID;
              
    return;
}

/**
* @Function:_LKT_IsMysqlOwnDB
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 判断是否是MYSQL自带的数据库
* @caution: 
*/
static int _LKT_IsMysqlOwnDB (const char* dbName)
{
    int i        = 0;
    int ownDBNum = sizeof (mysqlOwnDB) / sizeof (mysqlOwnDB[0]);
    int maxLen   = 0;
    int inDBLen  = 0;
    int ownDBLen = 0;
    int ownDB    = 0;
    const char *ownDBName  = NULL;
    
    for (i = 0; i < ownDBNum; i++)
    {
        ownDBName    = mysqlOwnDB[i];
        ownDBLen     = strlen(ownDBName);
        inDBLen      = strlen(dbName);
        
        maxLen = (inDBLen > ownDBLen) ? inDBLen : ownDBLen;
        
        if (0 == strncmp (dbName, ownDBName, maxLen))
        {
            ownDB = 1;
            break;
        }
    }

    return ownDB;
}

/**
* @Function:_LKT_GetColumnList
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 读取column列表
* @caution: 
*/
static int _LKT_GetColumnList (MYSQL *mysqlConn, const char* dbName, const char* tblName, COLUMN_S *columnList,  int *columnNum)
{
    if ((NULL == mysqlConn) || (NULL == dbName) || (NULL == tblName) || (NULL == columnList) || (NULL == columnNum))
    {
        return ERR_COMMON_INVALID_PARAM;
    }
    
    int ret = ERR_COMMON_SUCCESS;
    
    MYSQL_RES *res = NULL;
    do 
    {
        char sql[SQL_LEN] = {0};
        snprintf (sql, sizeof (sql), "select COLUMN_NAME from information_schema.COLUMNS where table_name = '%s' and table_schema = '%s'", tblName, dbName);
        
        ret = mysql_real_query (mysqlConn, sql, strlen(sql));
        if (ERR_COMMON_SUCCESS != ret)
        {
            ret = ERR_MYSQL_FAILED;
            dzlog_error ("mysql_real_query failed. SQL:%s. %s", sql, mysql_error(mysqlConn));
            
            break;
        }
        
        MYSQL_ROW row;
        
        res = mysql_store_result(mysqlConn);
        if (NULL == res)
        {
            ret = ERR_MYSQL_FAILED;
            dzlog_error ("mysql_store_result failed. SQL:%s. %s", sql, mysql_error(mysqlConn));
            break;
        }
        
        int colIdx          = 0;
        char *colName       = NULL;
        COLUMN_S *colInfo   = NULL;
        int colNum          = 0;
        while (NULL != (row = mysql_fetch_row(res)))
        {
            for(colIdx = 0; colIdx < mysql_num_fields(res); colIdx++)  
            {
                if (COLUMN_MAX_NUM == colNum)
                {
                    dzlog_error ("too many column from table[%s]", tblName);
                    continue;
                }
                
                colInfo = &columnList[colNum];
                colName = row[colIdx];
                snprintf (colInfo->columnName, sizeof (colInfo->columnName), "%s", colName);
                
                colNum++;
            }
        }
        
        if (ERR_COMMON_SUCCESS != ret)
        {
            break;
        }

        *columnNum = colNum;
    } while (0);
    
    mysql_free_result(res);  

    return ret;
}

/**
* @Function:_LKT_GetTableList
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 读取table列表
* @caution: 
*/
static int _LKT_GetTableList (MYSQL *mysqlConn, const char* dbName, TABLE_S *tblList, int *tblNum)
{
    if ((NULL == mysqlConn) || (NULL == dbName) || (NULL == tblList) || (NULL == tblNum))
    {
        return ERR_COMMON_INVALID_PARAM;
    }
    
    int ret = ERR_COMMON_SUCCESS;

    MYSQL_RES *res = NULL;
    do 
    {
        char sql[SQL_LEN] = {0};
        snprintf (sql, sizeof (sql), "SHOW TABLES FROM %s", dbName);
        
        ret = mysql_real_query (mysqlConn, sql, strlen(sql));
        if (ERR_COMMON_SUCCESS != ret)
        {
            ret = ERR_MYSQL_FAILED;
            dzlog_error ("mysql_real_query failed. SQL:%s. %s", sql, mysql_error(mysqlConn));
            
            break;
        }
        
        MYSQL_ROW row;
        
        res = mysql_store_result(mysqlConn);
        if (NULL == res)
        {
            ret = ERR_MYSQL_FAILED;
            dzlog_error ("mysql_store_result failed. SQL:%s. %s", sql, mysql_error(mysqlConn));
            break;
        }
        
        int tblIdx    = 0;
        char *tblName = NULL;
        TABLE_S *tblInfo = NULL;
        int tableNum = 0;
        int columnNum = 0;
        
        while (NULL != (row = mysql_fetch_row(res)))
        {  
            for(tblIdx = 0; tblIdx < mysql_num_fields(res); tblIdx++)  
            {
                if (TABLE_MAX_NUM == tableNum)
                {
                    dzlog_error ("too many table from database[%s]", dbName);
                    continue;
                }
                
                tblName = row[tblIdx];
                
                /* 策略中不存在的数据表不统计 */
                int tblIdx = -1;
                if (0 == _LKT_IsTblExistInStrategy (tblName, &tblIdx))
                {
                    continue;
                }

                tblInfo = &tblList[tableNum];
                snprintf (tblInfo->tableName, sizeof (tblInfo->tableName), "%s", tblName);

                columnNum = 0;
                ret = _LKT_GetColumnList (mysqlConn, dbName, tblName, tblInfo->columnList, &columnNum);
                if (ERR_COMMON_SUCCESS != ret)
                {
                    ret = ERR_MYSQL_FAILED;
                    dzlog_error ("get column list from table[%s] failed", tblName);
                    
                    break;
                }
                tblInfo->columnNum = columnNum;
                tableNum++;
            }
        }
        
        if (ERR_COMMON_SUCCESS != ret)
        {
            break;
        }
        
        *tblNum = tableNum;
    } while (0);
    
    mysql_free_result(res);  

    return ERR_COMMON_SUCCESS;
}

/**
* @Function:_LKT_GetDBList
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 读取db列表
* @caution: 
*/
static int _LKT_GetDBList (MYSQL *mysqlConn, DATABASE_INSTANCE_S *dbInstance)
{
    if ((NULL == mysqlConn) || (NULL == dbInstance))
    {
        return ERR_COMMON_INVALID_PARAM;
    }
    
    int ret = ERR_COMMON_SUCCESS;

    MYSQL_RES *res = NULL;
    do 
    {
        char *sql = "SHOW DATABASES";
        ret = mysql_real_query (mysqlConn, sql, strlen(sql));
        if (ERR_COMMON_SUCCESS != ret)
        {
            ret = ERR_MYSQL_FAILED;
            dzlog_error ("mysql_real_query failed. SQL:%s. %s", sql, mysql_error(mysqlConn));
            
            break;
        }
    
        MYSQL_ROW row;
        
        res = mysql_store_result(mysqlConn);
        if (NULL == res)
        {
            ret = ERR_MYSQL_FAILED;
            dzlog_error ("mysql_store_result failed. %s", mysql_error(mysqlConn));
            break;
        }
    
        int dbIdx           = 0;
        char *dbName        = NULL;
        int dbNum           = 0;
        int tblNum          = 0;
        DATABASE_S *dbInfo  = NULL;
        while (NULL != (row = mysql_fetch_row(res)))
        {  
            for(dbIdx = 0; dbIdx < mysql_num_fields(res); dbIdx++)  
            {
                if (DB_MAX_NUM == dbNum)
                {
                    dzlog_error ("too many database");
                    continue;
                }
                
                dbName = row[dbIdx];
                if (1 == _LKT_IsMysqlOwnDB (dbName))
                {
                    continue;
                }
                
                /* 策略中不存在的数据库不统计 */
                int dbIdx = -1;
                if (0 == _LKT_IsDBExistInStrategy (dbName, &dbIdx))
                {
                    continue;
                }

                
                dbInfo = &dbInstance->dbList[dbNum];
                snprintf (dbInfo->dbName, sizeof (dbInfo->dbName), "%s", dbName);
    
                tblNum = 0;
                ret = _LKT_GetTableList (mysqlConn, dbName, dbInfo->tableList, &tblNum);
                if (ERR_COMMON_SUCCESS != ret)
                {
                    ret = ERR_MYSQL_FAILED;
                    dzlog_error ("get table list from database[%s] failed", dbName);
                    
                    break;
                }
                dbNum++;
                dbInfo->tableNum = tblNum;
            }

            if (ERR_COMMON_SUCCESS != ret)
            {
                break;
            }
        }
                
        dbInstance->dbNum = dbNum;
    } while (0);
    
    mysql_free_result(res);

    return ERR_COMMON_SUCCESS;
}

/**
* @Function:_LKT_UpdateDBToSHM
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 读取数据库表结构信息
* @caution: 
*/
static int _LKT_UpdateDBToSHM (const char* host, int port, const char* user, const char* password, const char* db)
{
    if ((NULL == host) || (NULL == user) || (NULL == password))
    {
        return ERR_COMMON_INVALID_PARAM;
    }
    
    int ret      = ERR_COMMON_SUCCESS;
    DBS_S *pdbs  = _LKT_GetDBS ();
    
    MYSQL *mysqlConn = NULL;
    mysqlConn = mysql_init(NULL);
    if (NULL == mysqlConn)
    {
        dzlog_error ("mysql_init failed");
        return ERR_MYSQL_FAILED;
    }

    /* 设置MYSQL超时时间 */
    int connTimeOut = MYSQL_CONNECT_TIMEOUT;
    mysql_options(mysqlConn, MYSQL_OPT_CONNECT_TIMEOUT, &connTimeOut);
    
    int readTimeOut = MYSQL_READ_TIMEOUT;
    mysql_options(mysqlConn, MYSQL_OPT_READ_TIMEOUT, &readTimeOut);
    
    int writeTimeOut = MYSQL_WRITE_TIMEOUT;
    mysql_options(mysqlConn, MYSQL_OPT_WRITE_TIMEOUT, &writeTimeOut);
    
    DATABASE_INSTANCE_S *dbInstance = NULL;
    do
    {
        if (NULL == mysql_real_connect(mysqlConn, host, user, password, db, port, NULL, 0))
        {
            ret = ERR_MYSQL_FAILED;
            dzlog_error ("mysql_real_connect failed. Host:%s, port:%d, user:%s, password:%s, db:%s. %s", 
                        host, port, user, password, db, mysql_error(mysqlConn));

            break;
        }

        dbInstance = (DATABASE_INSTANCE_S*) malloc (sizeof (DATABASE_INSTANCE_S));
        if (NULL == dbInstance)
        {
            ret = ERR_COMMON_NO_MEM;
            dzlog_error ("no memory");
            break;
        }
        memset (dbInstance, 0, sizeof (sizeof (DATABASE_INSTANCE_S)));
        
        ret = _LKT_GetDBList (mysqlConn, dbInstance);
        if (ERR_COMMON_SUCCESS != ret)
        {
            ret = ERR_MYSQL_FAILED;
            dzlog_error ("get database list failed");
            
            break;
        }
        
        pthread_rwlock_wrlock (&pdbs->dataShm->rwLock);
        memcpy (&pdbs->dataShm->dbInstance, dbInstance, sizeof (pdbs->dataShm->dbInstance));
        pthread_rwlock_unlock (&pdbs->dataShm->rwLock);

        int i = 0;
        for (i = 0; i < dbInstance->dbNum; i++)
        {
            dzlog_debug ("-db:%s", dbInstance->dbList[i].dbName);

            DATABASE_S *pdb = &dbInstance->dbList[i];
            int j = 0;
            for (j = 0; j < pdb->tableNum; j++)
            {
                dzlog_debug ("--table:%s", pdb->tableList[j].tableName);

                int k = 0;
                TABLE_S *ptbl = &pdb->tableList[j];
                for (k = 0; k < ptbl->columnNum; k++)
                {
                    dzlog_debug ("---column:%s", ptbl->columnList[k].columnName);
                }
            }
        }
    }while (0);
    
    if (NULL != mysqlConn)
    {
        mysql_close(mysqlConn);
    }
    if (NULL != dbInstance)
    {
        free (dbInstance);
        dbInstance = NULL;
    }
    
    return ret;
}

/**
* @Function:_LKT_CreateJSONStrategy
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 创建JSON格式的策略
* @caution: 
*/
static cJSON* _LKT_CreateJSONStrategy (COLUMN_STRATEGY_S *colStrategy)
{    
    int ret = ERR_JSON_CREATE_FAILED;
    
    cJSON *strategy = cJSON_CreateObject();
    if (NULL == strategy)
    {
        dzlog_error("cJSON_CreateObject failed");
        return NULL;
    }
    
    DBS_S *pdbs       = _LKT_GetDBS ();
    DATA_SHM_S *shm   = pdbs->dataShm;
    APP_INFO_S *app   = &shm->appInfo;
    int tblIdx = colStrategy->tblIdx;
    int dbIdx  = colStrategy->dbIdx;
    char *tblName = app->tblStrategyList[tblIdx].tableName;
    char *dbName  = app->dbStrategyList[dbIdx].dbName;
    
    do 
    {
        cJSON_AddNumberToObject(strategy, IDENTIFY_STRATEGYID, colStrategy->strategyID);
        cJSON_AddStringToObject(strategy, IDENTIFY_COLUMNNAME, colStrategy->columnName);
        cJSON_AddStringToObject(strategy, IDENTIFY_TABLENAME, tblName);
        cJSON_AddStringToObject(strategy, IDENTIFY_DBNAME, dbName);
        cJSON_AddStringToObject(strategy, IDENTIFY_DATATYPE, colStrategy->type);
        cJSON_AddNumberToObject(strategy, IDENTIFY_STATE, colStrategy->state);
        if (0 == colStrategy->searchable)
        {
            cJSON_AddFalseToObject(strategy,IDENTIFY_SEARCHABLE);
        }
        else
        {
            cJSON_AddTrueToObject(strategy,IDENTIFY_SEARCHABLE);
        }
        cJSON_AddStringToObject(strategy, IDENTIFY_CHARSET, colStrategy->charset);
        
        cJSON *datakey = cJSON_CreateObject();
        if (NULL == datakey)
        {
            dzlog_error("cJSON_CreateObject failed");
            break;
        }
        cJSON_AddItemToObject(strategy, IDENTIFY_DATAKEY, datakey);
        
        cJSON_AddStringToObject(datakey, IDENTIFY_DATAKEY_KEY, colStrategy->dataKey.key);
        cJSON_AddStringToObject(datakey, IDENTIFY_DATAKEY_IV, colStrategy->dataKey.iv);
        cJSON_AddNumberToObject(datakey, IDENTIFY_DATAKEY_MAPPINGID, colStrategy->dataKey.mappingid);
        cJSON_AddNumberToObject(datakey, IDENTIFY_DATAKEY_ENABLEENCRYPT, colStrategy->dataKey.enableEncrypt);
        
        cJSON *cipher = cJSON_CreateObject();
        if (NULL == cipher)
        {
            dzlog_error("cJSON_CreateObject failed");
            break;
        }
        cJSON_AddItemToObject(strategy, IDENTIFY_CIPHER, cipher);
        
        cJSON_AddNumberToObject(cipher, IDENTIFY_CIPHER_ID, colStrategy->cipher.id);
        cJSON_AddStringToObject(cipher, IDENTIFY_CIPHER_ALGO, colStrategy->cipher.algo);
        cJSON_AddStringToObject(cipher, IDENTIFY_CIPHER_MODE, colStrategy->cipher.mode);
        cJSON_AddStringToObject(cipher, IDENTIFY_CIPHER_PADDING, colStrategy->cipher.padding);
        cJSON_AddNumberToObject(cipher, IDENTIFY_CIPHER_KEY_LEN, colStrategy->cipher.keyLen);
        cJSON_AddNumberToObject(cipher, IDENTIFY_CIPHER_IV_LEN, colStrategy->cipher.ivLen);

        ret = ERR_COMMON_SUCCESS;
    } while (0);

    if (ERR_COMMON_SUCCESS != ret)
    {
        cJSON_Delete(strategy);
        strategy = NULL;
    }

    return strategy;
}

/**
* @Function:_LKT_SaveStrategyToFile
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 保存策略到文件
* @caution: 
*/
static int _LKT_SaveStrategyToFile (const char* strategyFile)
{
    if (NULL == strategyFile)
    {
        dzlog_error("Invalid param");
        return ERR_COMMON_INVALID_PARAM;
    }
    
    int ret = ERR_JSON_CREATE_FAILED;
    
    DBS_S *pdbs       = _LKT_GetDBS ();
    DATA_SHM_S *shm   = pdbs->dataShm;
    APP_INFO_S *app   = &shm->appInfo;
    DBS_CONFIG_S *pdbsConfig = &pdbs->dbsConfig;

    cJSON *root       = NULL;

    /* 创建JSON */
    do 
    {
        root=cJSON_CreateObject();    
        if (NULL == root)
        {
            dzlog_error("cJSON_CreateObject failed");
            break;
        }
        cJSON_AddNumberToObject(root, IDENTIFY_LASTSYNC, shm->lastSyncTime);
        cJSON_AddStringToObject(root, IDENTIFY_APPKEY, pdbsConfig->accKeyID);
        /* 以下几类只是写到文件里，并不会在启动时加载 */
        cJSON_AddStringToObject(root, IDENTIFY_APPSCRET, pdbsConfig->accKeySecret);
        cJSON_AddStringToObject(root, IDENTIFY_APP_DATAKEY, shm->appInfo.appDataKey.key);
        cJSON_AddStringToObject(root, IDENTIFY_APP_DATAIV, shm->appInfo.appDataKey.iv);
        cJSON_AddStringToObject(root, IDENTIFY_MASTERKEY, shm->appInfo.companyKey.masterKey);
        
        cJSON *data = cJSON_CreateObject();
        if (NULL == data)
        {
            dzlog_error("cJSON_CreateObject failed");
            break;
        }
        cJSON_AddItemToObject(root, IDENTIFY_DATA, data);
        
        cJSON *strategies = cJSON_CreateArray ();
        if (NULL == strategies)
        {
            dzlog_error("cJSON_CreateArray failed");
            break;
        }
        cJSON_AddItemToObject(data, IDENTIFY_DBS_STRATEGY, strategies);

        int i           = 0;
        cJSON *strategy = NULL;
        COLUMN_STRATEGY_S *colStrategy = NULL;
        for (i = 0; i < app->columnNum; i++)
        {
            colStrategy = &app->columnStrategyList[i];

            strategy = _LKT_CreateJSONStrategy (colStrategy);
            if (NULL== strategy)
            {
                dzlog_error("create json strategy failed");
                break;
            }
            
            cJSON_AddItemToArray(strategies, strategy);  
        }

        ret = ERR_COMMON_SUCCESS;
    }while (0);
    
    if (ERR_COMMON_SUCCESS != ret)
    {
        cJSON_Delete(root);
        return ret;
    }

    /* Free the char* when finished. */
    char *pJSON = cJSON_Print(root);
    int jsonLen = strlen(pJSON);

    /* JSON写入到文件 */
    FILE *fp = NULL;
    do 
    {
        fp = fopen (strategyFile, "wb");
        if (NULL == fp)
        {
            dzlog_error("open file[%s] failed. %s", strategyFile, strerror(errno));
            ret = ERR_COMMON_SYSFAILED;
            break;
        }
        int writeLen = fwrite (pJSON, 1, jsonLen, fp);
        if (writeLen != jsonLen)
        {
            dzlog_error("write file[%s] failed. %s", strategyFile, strerror(errno));
            ret = ERR_COMMON_SYSFAILED;
            break;
        }
        else if (0 == writeLen)
        {
            dzlog_error("write file[%s] failed. %s", strategyFile, strerror(errno));
            ret = ERR_COMMON_SYSFAILED;
        }
    } while (0);

    if (NULL != fp)
    {
        fclose(fp);
    }
    
    free(pJSON);
    cJSON_Delete(root);

    return ret;
}

/**
* @Function:_LKT_StartDeltaUpdateThread
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 增量更新
* @caution: 
*/
static void* _LKT_DeltaUpdate (void *arg)
{
    int ret                  = ERR_COMMON_SUCCESS;
    int hasDelta             = 0;
    int firstUpdate          = 1;
    DBS_S *pdbs              = _LKT_GetDBS ();
    DBS_CONFIG_S *pdbsConfig = &pdbs->dbsConfig;
    
    while (1)
    {
        if (0 == firstUpdate)
        {
            sleep (UPDATE_INTERVAL);
        }
        else
        {
            /* 第一次更新，不需要等待 */
            firstUpdate = 0;
        }
        
        /* 检查策略是否有更新 */
        hasDelta = _LKT_HasDelta ();
        if (0 == hasDelta)
        {
            continue;
        }
        dzlog_info ("dbs strategy has delta");

        /* 获取增量更新数据 */
        ret = _LKT_GetDeltaAndUpdateStrategyToSHM ();
        if (ERR_COMMON_SUCCESS != ret)
        {
            dzlog_error ("Get delta failed");
            continue;
        }

        /* 更新数据库信息 */
        ret = _LKT_UpdateDBToSHM (pdbsConfig->dbsHost, pdbsConfig->dbsPort, pdbsConfig->dbsUser, pdbsConfig->dbsPassword, NULL);
        if (ERR_COMMON_SUCCESS != ret)
        {
            dzlog_error ("Refresh strategy to SHM failed");
            continue;
        }

        /* 策略保存到文件 */
        ret = _LKT_SaveStrategyToFile (STRATEGY_FILE);
        if (ERR_COMMON_SUCCESS != ret)
        {
            dzlog_error ("Refresh strategy to file failed");
            continue;
        }
    }
    
    return NULL; 
}

/**
* @Function:_LKT_StartDeltaUpdateThread
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 启动增量更新线程
* @caution: 
*/
static int _LKT_StartDeltaUpdateThread (void)
{
    int ret             = 0;
    pthread_t thread    = 0;
    pthread_attr_t attr;

    
    ret = pthread_attr_init (&attr);
    if (ERR_COMMON_SUCCESS != ret)
    {
        dzlog_error ("pthread_attr_init failed. %s",  strerror(errno));
        return ERR_COMMON_SYSFAILED;
    }
    
    ret = pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
    if (ERR_COMMON_SUCCESS != ret)
    {
        dzlog_error ("pthread_attr_setdetachstate failed. %s",  strerror(errno));
        return ERR_COMMON_SYSFAILED;
    }
    
    ret = pthread_create(&thread, &attr, _LKT_DeltaUpdate, NULL);
    if (ERR_COMMON_SUCCESS != ret)
    {
        dzlog_error ("pthread_create failed. %s",  strerror(errno));
        return ERR_COMMON_SYSFAILED;
    }
    
    pthread_attr_destroy (&attr);

    return ret;
}

/**
* @Function:_LKT_UpdateStrategyToSHM
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 更新策略到内存中
* @caution: 
*/
static int _LKT_UpdateStrategyToSHM (DELTA_S *pdelta)
{
    if (NULL == pdelta)
    {
        dzlog_error("Invalid param");
        return ERR_COMMON_INVALID_PARAM;
    }
    
    int ret = ERR_COMMON_SUCCESS;
    
    DBS_S *pdbs = _LKT_GetDBS ();

    if (0 != pdelta->lastSync)
    {
        pdbs->dataShm->lastSyncTime = pdelta->lastSync;
    }
    
    int i                    = 0;
    int strategyNum          = pdelta->strategyNum;
    STRATEGY_S *strategyList = pdelta->strategies;
    int colIdx               = -1;

    pthread_rwlock_wrlock (&pdbs->dataShm->rwLock);
    for (i = 0; i < strategyNum; i++)
    {
        colIdx = -1;
        
        int exist = _LKT_IsStrategyExist (strategyList[i].colName, 
                                          strategyList[i].tblName,
                                          strategyList[i].dbName,
                                          &colIdx);
        if (1 == exist)
        {
            _LKT_UpdateStrategy(&strategyList[i], colIdx);
        }
        else
        {
            ret = _LKT_InsertStrategy (&strategyList[i]);
            if (ERR_COMMON_SUCCESS != ret)
            {
                dzlog_error("Insert strategyIdx[%d] faild, dbName:%s, tblName:%s, colName:%s", 
                            i, 
                            strategyList[i].dbName,strategyList[i].tblName, strategyList[i].colName);
                break;
            }
        }
    }
    pthread_rwlock_unlock (&pdbs->dataShm->rwLock);

    return ret;
}

/**
* @Function:_LKT_SignalHandle
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 信号处理函数
* @caution: 
*/
static void _LKT_SignalHandle(int sig_num)  
{  
    if(sig_num == SIGUSR1)  
    {
        /* 当接收到SIGUSR1信号后，输出内存泄漏报告 */
        print_memory_leak_report ();
    }

    return;
}  

/**
* @Function:_LKT_InstallSignal
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 安装信号捕捉
* @caution: 
*/
static sighandler_t _LKT_InstallSignal (void)
{
    return signal(SIGUSR1, _LKT_SignalHandle);  
}

/**
* @Function:_LKT_ParseJSONAppDataKey
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 解析JSON格式的APPDataKey
* @caution: 
*/
static int _LKT_ParseJSONAppDataKey (const char* json, APP_DATAKEY_S *appDataKey)
{
    int ret = ERR_COMMON_SUCCESS;
    
    cJSON *root = cJSON_Parse(json);
    if (NULL == root)
    {
        dzlog_error( "cJSON_Parse failed, json:%s", json);
        return ERR_JSON_PARSE_FAILED;
    }
    
    do 
    {        
        char *node  = NULL;
        cJSON *tmp  = NULL;
        cJSON *data = NULL;
        
        node = IDENTIFY_DATA;
        data = cJSON_GetObjectItem(root, node);
        if (NULL == data)
        {
            dzlog_error("can't find %s, json:%s", node, json);
            ret = ERR_INVALID_APPDATAKEY;
            break;
        }
        
        node = IDENTIFY_APP_DATAKEY;
        tmp = cJSON_GetObjectItem(data, node);
        if (NULL == tmp)
        {
            dzlog_error("can't find %s, json:%s", node, json);
            ret = ERR_INVALID_APPDATAKEY;
            break;
        }
        snprintf (appDataKey->key, KEY_MAX_LEN, "%s", tmp->valuestring);
        
        node = IDENTIFY_APP_DATAIV;
        tmp = cJSON_GetObjectItem(data, node);
        if (NULL == tmp)
        {
            dzlog_error("can't find %s, json:%s", node, json);
            ret = ERR_INVALID_APPDATAKEY;
            break;
        }
        snprintf (appDataKey->iv, IV_MAX_LEN, "%s", tmp->valuestring);

    }while (0);
    
    /* 资源回收 */
    cJSON_Delete (root);

    return ret;
}

/**
* @Function:_LKT_UpdateAPPDataKeyToSHM
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 更新APPDATAKEY到SHM
* @caution: 
*/
static void _LKT_UpdateAPPDataKeyToSHM (APP_DATAKEY_S *appDataKey)
{
    
    DBS_S *pdbs = _LKT_GetDBS ();
    
    memcpy (&pdbs->dataShm->appInfo.appDataKey, appDataKey, sizeof (APP_DATAKEY_S));

    return;
}


/**
* @Function:_LKT_RetriveAppDataKey 
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 接收APP Data KEY信息 
* @caution: 
*/
static int _LKT_RetriveAppDataKey (void)
{
    int ret = ERR_COMMON_SUCCESS;

    DBS_S *pdbs              = _LKT_GetDBS ();

    char appKey[HTTP_HEADER_LEN] = {0};
    snprintf (appKey, sizeof (appKey), "%s=%s", IDENTIFY_APPKEY, pdbs->dbsConfig.accKeyID);

    const char *api   = API_APP_DATAKEY;
    char url[URL_LEN] = {0};
    snprintf (url, sizeof (url), 
            "%s://%s:%d%s?%s", HTTP_PROTOCOL, pdbs->dbsConfig.host, pdbs->dbsConfig.port, 
            api, appKey);

    /* 添加签名校验 */
    int i = 0;
    const char* headers[3] = {0};
    {
        /* 获取GMT */
        char GMTime[TIME_LEN] = {0};
        (void) _LKT_GetGmtTime (GMTime);

        /* 添加header -> appKey2 */
        char appKey2[HTTP_HEADER_LEN] = {0};
        memset (appKey2, 0, sizeof (appKey2));
        {
            snprintf (appKey2, sizeof (appKey2), "%s: %s", IDENTIFY_APPKEY, pdbs->dbsConfig.accKeyID);
            headers[i++] = appKey2;
        }

        /* 添加header -> data */
        char date[DATE_LEN]  = {0};
        {
            snprintf ((char*) date, sizeof (date), "%s: %s", IDENTIFY_DATE, GMTime);
            headers[i++] = date;
        }

        /* 添加header -> Authorization */
        char author[AUTHOR_LEN] = {0};
        char sign[AUTHOR_LEN] = {0};
        {
            ret = _LKT_GenAuthorString (pdbs->dbsConfig.accKeyID, pdbs->dbsConfig.accKeySecret, GMTime, author, API_APP_DATAKEY);
            if (ERR_COMMON_SUCCESS != ret)
            {
                dzlog_error( "generate author string failed");
                return ret;
            }

            snprintf (sign, sizeof (sign), "%s: %s", IDENTIFY_AUTHOR, author);
            headers[i++] = sign;
        }
    }
    
    long responseCode       = 0;
    char *responseContent   = NULL;
    ret = _LKT_SendHTTPRequest (url, headers, i, NULL, &responseCode, &responseContent);
    if (ERR_COMMON_SUCCESS != ret)
    {
        dzlog_error ("Send HTTP Request failed, url:%s responseCode:%ld, responseContent:%s", 
                    url, responseCode, responseContent);
        return ret;
    }
    dzlog_debug("Retrive app data key url:%s, code[%ld], response content[%s]", url, responseCode, responseContent); 
    
    APP_DATAKEY_S appDataKey;
    memset (&appDataKey, 0, sizeof (appDataKey));
    
    ret = _LKT_ParseJSONAppDataKey (responseContent, &appDataKey);
    if (ERR_COMMON_SUCCESS != ret)
    {
        dzlog_error ("Pase JSON app data key failed");
        return ret;
    }
    dzlog_info("app data key [%s], app data iv :[%s]", appDataKey.key, appDataKey.iv); 
    
    (void) _LKT_UpdateAPPDataKeyToSHM (&appDataKey);

    return ERR_COMMON_SUCCESS;
}

/**
* @Function:_LKT_ParseJSONCompanyKey
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 解析JSON格式的CompanKey
* @caution: 
*/
static int _LKT_ParseJSONCompanyKey (const char* json, COMPANY_KEY_S *companyKey)
{
    int ret = ERR_COMMON_SUCCESS;
    
    cJSON *root = cJSON_Parse(json);
    if (NULL == root)
    {
        dzlog_error( "cJSON_Parse failed, json:%s", json);
        return ERR_JSON_PARSE_FAILED;
    }
    
    do 
    {        
        char *node  = NULL;
        cJSON *tmp  = NULL;
        
        node = IDENTIFY_DATA;
        cJSON *data = cJSON_GetObjectItem(root, node);
        if (NULL == data)
        {
            dzlog_error("can't find %s, json:%s", node, json);
            ret = ERR_INVALID_STRATEGY;
            break;
        }
         
        node = IDENTIFY_MASTERKEY;
        tmp = cJSON_GetObjectItem(data, node);
        if (NULL == tmp)
        {
            dzlog_error("can't find %s, json:%s", node, json);
            ret = ERR_INVALID_STRATEGY;
            break;
        }
        snprintf (companyKey->masterKey, COMPANY_MASTER_KEY_LEN, "%s", tmp->valuestring);
        
        node = IDENTIFY_PV;
        tmp = cJSON_GetObjectItem(data, node);
        if (NULL == tmp)
        {
            dzlog_error("can't find %s, json:%s", node, json);
            ret = ERR_INVALID_STRATEGY;
            break;
        }
        snprintf (companyKey->pv, COMPANY_PV_LEN, "%s", tmp->valuestring);

    }while (0);
    
    /* 资源回收 */
    cJSON_Delete (root);

    return ret;
}

/**
* @Function:_LKT_UpdateAppSecretToSHM
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 更新appSecret到SHM
* @caution: 
*/
static void _LKT_UpdateAppSecretToSHM ()
{
    
    DBS_S *pdbs = _LKT_GetDBS ();
    DBS_CONFIG_S *pdbsConfig = &pdbs->dbsConfig;

    snprintf (pdbs->dataShm->appInfo.appAccessSecret, 
              ACCESSKEY_SECRET_MAX_LEN, "%s", pdbsConfig->accKeySecret);
        
    return;
}


/**
* @Function:_LKT_UpdateCompanyKeyToSHM
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 更新CompanyKey到SHM
* @caution: 
*/
static void _LKT_UpdateCompanyKeyToSHM (COMPANY_KEY_S *companyKey)
{
    
    DBS_S *pdbs = _LKT_GetDBS ();
    
    memcpy (&pdbs->dataShm->appInfo.companyKey, companyKey, sizeof (COMPANY_KEY_S));
        
    return;
}

/**
* @Function:_LKT_RetriveCompanKey
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 接收COMPANY KEY信息 
* @caution: 
*/
static int _LKT_RetriveCompanKey (void)
{
    int ret = ERR_COMMON_SUCCESS;
    
    DBS_S *pdbs              = _LKT_GetDBS ();
    
    char cursor[HTTP_HEADER_LEN] = {0};
    snprintf (cursor, sizeof (cursor), "%s=%d", IDENTIFY_CURSOR, 0);
    
    char lastSync[HTTP_HEADER_LEN] = {0};
    snprintf (lastSync, sizeof (lastSync), "%s=%d", IDENTIFY_LASTSYNC, 0);
    
    char appKey[HTTP_HEADER_LEN] = {0};
    snprintf (appKey, sizeof (appKey), "%s=%s", IDENTIFY_APPKEY, pdbs->dbsConfig.accKeyID);
    
    char companyID[HTTP_HEADER_LEN] = {0};
    snprintf(companyID, sizeof(companyID), "%s=%s%s",IDENTIFY_COMPANY, pdbs->dbsConfig.accKeyID,pdbs->dbsConfig.accKeyID);

    char maxCount[HTTP_HEADER_LEN] = {0};
    snprintf (maxCount, sizeof (maxCount), "%s=%d", IDENTIFY_MAXCOUNT, QUERY_PAGE_COUNT);
    
    const char *api   = API_COMPANY_KEY;
    char url[URL_LEN] = {0};
    snprintf (url, sizeof (url), 
              "%s://%s:%d%s?%s&%s&%s&%s&%s", HTTP_PROTOCOL, pdbs->dbsConfig.host, pdbs->dbsConfig.port, 
               api, appKey, companyID, lastSync, cursor,maxCount);
    
    dzlog_debug("companyKey  request[%s]",url);	
    /* 添加签名校验 */
    int i = 0;
    const char* headers[3] = {0};
    {

        /* 获取GMT */
        char GMTime[TIME_LEN] = {0};
        (void) _LKT_GetGmtTime (GMTime);

        /* 添加header -> appKey2 */
        char appKey2[HTTP_HEADER_LEN] = {0};
        memset (appKey2, 0, sizeof (appKey2));
        {
            snprintf (appKey2, sizeof (appKey2), "%s: %s", IDENTIFY_APPKEY, pdbs->dbsConfig.accKeyID);
            headers[i++] = appKey2;
        }

        /* 添加header -> data */
        char date[DATE_LEN]  = {0};
        {
            snprintf ((char*) date, sizeof (date), "%s: %s", IDENTIFY_DATE, GMTime);
            headers[i++] = date;
        }

        /* 添加header -> Authorization */
        char author[AUTHOR_LEN] = {0};
        char sign[AUTHOR_LEN] = {0};
        {
            ret = _LKT_GenAuthorString (pdbs->dbsConfig.accKeyID, pdbs->dbsConfig.accKeySecret, GMTime, author, API_COMPANY_KEY);
            if (ERR_COMMON_SUCCESS != ret)
            {
                dzlog_error( "generate author string failed");
                return ret;
            }

            snprintf (sign, sizeof (sign), "%s: %s", IDENTIFY_AUTHOR, author);
            headers[i++] = sign;
        }
    }

    long responseCode       = 0;
    char *responseContent   = NULL;
    ret = _LKT_SendHTTPRequest (url, headers, i, NULL, &responseCode, &responseContent);
    if (ERR_COMMON_SUCCESS != ret)
    {
        dzlog_error ("Send HTTP Request failed, retrive companKey failed,url:%s, code:%ld, responseContent:%s", 
                url, responseCode, responseContent);
        return ret;
    }
    dzlog_debug("companyKey  request url:%s, code:%ld,  response content[%s]", url, responseCode, responseContent);	
    
    COMPANY_KEY_S *companKey = (COMPANY_KEY_S*) malloc (sizeof (COMPANY_KEY_S));
    if (NULL == companKey)
    {
        ret = ERR_COMMON_SUCCESS;
        return ret;
    }
    memset (companKey, 0, sizeof (COMPANY_KEY_S));
    
    ret = _LKT_ParseJSONCompanyKey (responseContent, companKey);
    if (ERR_COMMON_SUCCESS != ret)
    {
        free (companKey);
        dzlog_error ("Parse JSON companyKey failed");
        return ret;
    }
    
    (void) _LKT_UpdateCompanyKeyToSHM (companKey);
    
    free (companKey);
    
    return ERR_COMMON_SUCCESS;
}

static int _LKT_EnsureSingleProcess (void)
{
    int  fd = 0;
    char buf[32] = {0};

    //打开后不能close fd, 否则文件锁失效
    fd = open(DATASYNC_LOCK_FILE, O_RDWR | O_CREAT, 0666);
    if(-1 == fd)
    {
        dzlog_error("open file[%s] failed. %s", DATASYNC_LOCK_FILE, strerror(errno));
        return ERR_COMMON_FAILED;
    }
    
    read(fd, buf, sizeof(buf));

    //进程异常退出时, 关闭fd, 文件锁自动释放
    if(0 != flock(fd, LOCK_EX | LOCK_NB))
    {
        if(EWOULDBLOCK == errno)
        {
            dzlog_error("datasync process is already running, pid[%s]",  buf);
            return ERR_COMMON_FAILED;
        }

        return ERR_COMMON_SYSFAILED;
    }
    else 
    {
        snprintf(buf, sizeof(buf), "%d", getpid());
        lseek(fd, 0, SEEK_SET);
        write(fd, buf, strlen(buf));
        return ERR_COMMON_SUCCESS;
    }
}


/**
* @Function:_LKT_ValidateServer
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 验证服务器是否正常
* @caution: 
*/
static int _LKT_ValidateServer (DBS_CONFIG_S *dbsConfig)
{
    int ret = ERR_COMMON_SUCCESS;

    int i = 0;
    const char* headers[3] = {0};
    DBS_S *pdbs = _LKT_GetDBS ();
    
    /* 获取GMT */
    char GMTime[TIME_LEN] = {0};
    (void) _LKT_GetGmtTime (GMTime);
    
    /* 添加header -> appKey */
    char appKey[HTTP_HEADER_LEN] = {0};
    {
        snprintf (appKey, sizeof (appKey), "%s: %s", IDENTIFY_APPKEY, dbsConfig->accKeyID);
        headers[i++] = appKey;
    }
    
    /* 添加header -> data */
    char date[DATE_LEN]  = {0};
    {
        snprintf ((char*) date, sizeof (date), "%s: %s", IDENTIFY_DATE, GMTime);
        headers[i++] = date;
    }
    
    /* 添加header -> Authorization */
    char author[AUTHOR_LEN] = {0};
    char sign[AUTHOR_LEN] = {0};
    {
        ret = _LKT_GenAuthorString (dbsConfig->accKeyID, dbsConfig->accKeySecret, GMTime, author, API_APP_CONSTANTS);
        if (ERR_COMMON_SUCCESS != ret)
        {
            dzlog_error( "generate author string failed");
            return ret;
        }
        
        snprintf (sign, sizeof (sign), "%s: %s", IDENTIFY_AUTHOR, author);
        headers[i++] = sign;
    }
    
    char httpURL[URL_LEN] = {0};
    const char *api = API_APP_CONSTANTS;
    snprintf (httpURL, sizeof (httpURL), 
              "%s://%s:%d%s", HTTP_PROTOCOL, pdbs->dbsConfig.host, pdbs->dbsConfig.port, api);
    
    long responseCode      = 0;
    char *responseContent = NULL;
    ret = _LKT_SendHTTPRequest (httpURL, headers, i, NULL, &responseCode, &responseContent);
    if (ERR_COMMON_SUCCESS != ret)
    {
        dzlog_error ("Send HTTP Request failed, url:%s, responseCode:%ld, responseContent:%s", 
                        httpURL, responseCode, responseContent);
        return ret;
    }

    dzlog_debug("validate server url:%s code:%ld, reponse content[%s]", httpURL, responseCode, responseContent);

    /* 校验appkey, appsecret */
    {
        char *json                        = responseContent;
        char appKey[ACCESSKEY_ID_MAX_LEN]        = {0};
        char appSecret[ACCESSKEY_SECRET_MAX_LEN] = {0};
        
        ret = _LKT_GetAccessKey (json, appKey, appSecret);
        if (ERR_COMMON_SUCCESS != ret)
        {
            dzlog_error( "get app constants config failed");
            return ret;
        }
        dzlog_info("Retrive appKey:%s, appSecret:%s", appKey, appSecret);
        
        if ((0 != strncmp (appKey, dbsConfig->accKeyID, sizeof (appKey))) &&
            (0 != strncmp (appSecret, dbsConfig->accKeySecret, sizeof (appSecret))))
        {
            dzlog_info("validate app info failed. appKey:%s, appSecret:%s", appKey, appSecret);
            return ERR_VALIDATE_SERVER_FAILED;
        }
    }

    return ret;
}

int main(int argc, const char* argv[])
{
    int ret = ERR_COMMON_SUCCESS;

    DBS_S *pdbs = _LKT_GetDBS ();
    memset (pdbs, 0, sizeof (DBS_S));
    
    DBS_CONFIG_S *pdbsConfig = &pdbs->dbsConfig;

    /* 只允许一个进程运行 */
    ret =_LKT_EnsureSingleProcess ();
    if (ERR_COMMON_SUCCESS != ret) 
    {
        return ret;
    }

    /* 初始化日志 */
    ret = _LKT_InitZlog ();
    if (ERR_COMMON_SUCCESS != ret) 
    {
        return ret;
    }
    dzlog_info("Build time:%s %s", __DATE__, __TIME__);

    dzlog_info("Init log success");

    /* 信号捕捉 */
    (void) _LKT_InstallSignal ();

    do 
    {
        /* 初始化OpenSSL */
        ret = LKT_InitOpenSSL ();
        if (ERR_COMMON_SUCCESS != ret)
        {
            dzlog_error ("Init OpenSSL failed");
            break;
        }        
        
        /* 初始化curl */
        ret = _LKT_InitCurl ();
        if (ERR_COMMON_SUCCESS != ret)
        {
            dzlog_error ("Init Curl failed");
            break;
        }
        
        /* 创建共享内存 */
        ret = _LKT_CreateSharedMemory ();
        if (ERR_COMMON_SUCCESS != ret)
        {
            dzlog_error ("Create shared memory failed");
            break;
        }
        
        /* 创建共享内存读写锁 */
        ret = _LKT_CreateWRLock ();
        if (ERR_COMMON_SUCCESS != ret)
        {
            dzlog_error ("Create read write lock failed");
            break;
        }
        
        /* 初始化dbs配置 */
        ret = _LKT_InitDBSConfig (pdbsConfig);
        if (ERR_COMMON_SUCCESS != ret) 
        {
            dzlog_error ("Init dbs config failed");
            break;  
        }
        dzlog_info("Init dbs config success");

        /* 验证 Server */
        ret = _LKT_ValidateServer (pdbsConfig);
        if (ERR_COMMON_SUCCESS != ret)
        {
            dzlog_error ("Validate LOCKet server failed");
            break;
        }
        dzlog_info("Validate LOCKet server OK");

        /* 获取应用级加密的key和iv */
        ret = _LKT_RetriveAppDataKey ();
        if (ERR_COMMON_SUCCESS != ret)
        {
            dzlog_error("Retrive app data key failed");
            break;
        } 
        dzlog_info("Retrive app data key success");

        /* 接收company KEY信息  */
        int firstRetrive = 1;
        for(;;)
        {
            ret = _LKT_RetriveCompanKey ();
            if (ERR_COMMON_SUCCESS == ret)
            {
                dzlog_info("Retrive companyKey success");
                break;
            } 
            
            if(firstRetrive)
            {
                dzlog_info("Wating for companKey");
            }
                
            firstRetrive = 0;
            sleep(60);
        }

        /* 更新AppSecret到SHM中  */
        _LKT_UpdateAppSecretToSHM ();
        
        /* 从文件内读取策略,并且更新到SHM中 */
        ret = _LKT_UpdateStrategyToSHMFromFile (STRATEGY_FILE);
        if (ERR_COMMON_SUCCESS != ret)
        {
            dzlog_info ("Load strategy failed, may be file not existed or invalid file");
        }
        else
        {
            dzlog_info ("Load local strategy cache success");

            /* 从数据库中获取策略对应表的表结构 */
            ret = _LKT_UpdateDBToSHM (pdbsConfig->dbsHost, pdbsConfig->dbsPort, 
                                      pdbsConfig->dbsUser, pdbsConfig->dbsPassword, NULL);
            if (ERR_COMMON_SUCCESS != ret)
            {
                dzlog_error ("Refresh strategy to SHM failed");
                break;
            }
        }
        
        /* 启动增量更新线程 */
        ret = _LKT_StartDeltaUpdateThread ();
        if (ERR_COMMON_SUCCESS != ret)
        {
            dzlog_error ("Start delta update thread failed");
            break;
        }
    } while (0);

    if (ERR_COMMON_SUCCESS != ret)
    {
        printf("Start datasync server failed ......!\n");
        dzlog_error("Start datasync server failed ......!");
    }
    else
    {
        printf("DBS start success\n");
        dzlog_info("DBS start success");
        _LKT_Running ();
    }

    /* 释放各种资源 */
    _LKT_ReleaseWRLock ();
    _LKT_ReleaseSharedMemory ();
    LKT_ReleaseOpenSSL ();
    _LKT_ReleaseZlog ();
    _LKT_ReleaseCurl ();
    
    return 0;
}

